metro-file-map 0.80.8 → 0.80.10
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/package.json +2 -1
- package/src/Watcher.js +1 -0
- package/src/Watcher.js.flow +1 -0
- package/src/crawlers/node/index.js +67 -37
- package/src/crawlers/node/index.js.flow +63 -42
- package/src/flow-types.js.flow +1 -0
- package/src/lib/RootPathUtils.js +23 -7
- package/src/lib/RootPathUtils.js.flow +28 -7
- package/src/lib/TreeFS.js +76 -34
- package/src/lib/TreeFS.js.flow +90 -36
- package/src/watchers/RecrawlWarning.js +1 -1
- package/src/watchers/RecrawlWarning.js.flow +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metro-file-map",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.10",
|
|
4
4
|
"description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"anymatch": "^3.0.3",
|
|
17
17
|
"debug": "^2.2.0",
|
|
18
18
|
"fb-watchman": "^2.0.0",
|
|
19
|
+
"flow-enums-runtime": "^0.0.6",
|
|
19
20
|
"graceful-fs": "^4.2.4",
|
|
20
21
|
"invariant": "^2.2.4",
|
|
21
22
|
"jest-worker": "^29.6.3",
|
package/src/Watcher.js
CHANGED
|
@@ -86,6 +86,7 @@ class Watcher extends _events.default {
|
|
|
86
86
|
const crawlerOptions = {
|
|
87
87
|
abortSignal: options.abortSignal,
|
|
88
88
|
computeSha1: options.computeSha1,
|
|
89
|
+
console: options.console,
|
|
89
90
|
includeSymlinks: options.enableSymlinks,
|
|
90
91
|
extensions: options.extensions,
|
|
91
92
|
forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
|
package/src/Watcher.js.flow
CHANGED
|
@@ -103,6 +103,7 @@ export class Watcher extends EventEmitter {
|
|
|
103
103
|
const crawlerOptions: CrawlerOptions = {
|
|
104
104
|
abortSignal: options.abortSignal,
|
|
105
105
|
computeSha1: options.computeSha1,
|
|
106
|
+
console: options.console,
|
|
106
107
|
includeSymlinks: options.enableSymlinks,
|
|
107
108
|
extensions: options.extensions,
|
|
108
109
|
forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
|
|
@@ -52,7 +52,15 @@ function _interopRequireDefault(obj) {
|
|
|
52
52
|
return obj && obj.__esModule ? obj : { default: obj };
|
|
53
53
|
}
|
|
54
54
|
const debug = require("debug")("Metro:NodeCrawler");
|
|
55
|
-
function find(
|
|
55
|
+
function find(
|
|
56
|
+
roots,
|
|
57
|
+
extensions,
|
|
58
|
+
ignore,
|
|
59
|
+
includeSymlinks,
|
|
60
|
+
rootDir,
|
|
61
|
+
console,
|
|
62
|
+
callback
|
|
63
|
+
) {
|
|
56
64
|
const result = new Map();
|
|
57
65
|
let activeCalls = 0;
|
|
58
66
|
const pathUtils = new _RootPathUtils.RootPathUtils(rootDir);
|
|
@@ -66,43 +74,47 @@ function find(roots, extensions, ignore, includeSymlinks, rootDir, callback) {
|
|
|
66
74
|
(err, entries) => {
|
|
67
75
|
activeCalls--;
|
|
68
76
|
if (err) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
activeCalls++;
|
|
85
|
-
fs.lstat(file, (err, stat) => {
|
|
86
|
-
activeCalls--;
|
|
87
|
-
if (!err && stat) {
|
|
88
|
-
const ext = path.extname(file).substr(1);
|
|
89
|
-
if (stat.isSymbolicLink() || extensions.includes(ext)) {
|
|
90
|
-
result.set(pathUtils.absoluteToNormal(file), [
|
|
91
|
-
"",
|
|
92
|
-
stat.mtime.getTime(),
|
|
93
|
-
stat.size,
|
|
94
|
-
0,
|
|
95
|
-
"",
|
|
96
|
-
null,
|
|
97
|
-
stat.isSymbolicLink() ? 1 : 0,
|
|
98
|
-
]);
|
|
99
|
-
}
|
|
77
|
+
console.warn(
|
|
78
|
+
`Error "${
|
|
79
|
+
err.code ?? err.message
|
|
80
|
+
}" reading contents of "${directory}", skipping. Add this directory to your ignore list to exclude it.`
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
entries.forEach((entry) => {
|
|
84
|
+
const file = path.join(directory, entry.name.toString());
|
|
85
|
+
if (ignore(file)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (entry.isSymbolicLink() && !includeSymlinks) {
|
|
89
|
+
return;
|
|
100
90
|
}
|
|
101
|
-
if (
|
|
102
|
-
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
search(file);
|
|
93
|
+
return;
|
|
103
94
|
}
|
|
95
|
+
activeCalls++;
|
|
96
|
+
fs.lstat(file, (err, stat) => {
|
|
97
|
+
activeCalls--;
|
|
98
|
+
if (!err && stat) {
|
|
99
|
+
const ext = path.extname(file).substr(1);
|
|
100
|
+
if (stat.isSymbolicLink() || extensions.includes(ext)) {
|
|
101
|
+
result.set(pathUtils.absoluteToNormal(file), [
|
|
102
|
+
"",
|
|
103
|
+
stat.mtime.getTime(),
|
|
104
|
+
stat.size,
|
|
105
|
+
0,
|
|
106
|
+
"",
|
|
107
|
+
null,
|
|
108
|
+
stat.isSymbolicLink() ? 1 : 0,
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (activeCalls === 0) {
|
|
113
|
+
callback(result);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
104
116
|
});
|
|
105
|
-
}
|
|
117
|
+
}
|
|
106
118
|
if (activeCalls === 0) {
|
|
107
119
|
callback(result);
|
|
108
120
|
}
|
|
@@ -121,6 +133,7 @@ function findNative(
|
|
|
121
133
|
ignore,
|
|
122
134
|
includeSymlinks,
|
|
123
135
|
rootDir,
|
|
136
|
+
console,
|
|
124
137
|
callback
|
|
125
138
|
) {
|
|
126
139
|
const extensionClause = extensions.length
|
|
@@ -175,6 +188,7 @@ function findNative(
|
|
|
175
188
|
}
|
|
176
189
|
module.exports = async function nodeCrawl(options) {
|
|
177
190
|
const {
|
|
191
|
+
console,
|
|
178
192
|
previousState,
|
|
179
193
|
extensions,
|
|
180
194
|
forceNodeFilesystemAPI,
|
|
@@ -204,9 +218,25 @@ module.exports = async function nodeCrawl(options) {
|
|
|
204
218
|
resolve(difference);
|
|
205
219
|
};
|
|
206
220
|
if (useNativeFind) {
|
|
207
|
-
findNative(
|
|
221
|
+
findNative(
|
|
222
|
+
roots,
|
|
223
|
+
extensions,
|
|
224
|
+
ignore,
|
|
225
|
+
includeSymlinks,
|
|
226
|
+
rootDir,
|
|
227
|
+
console,
|
|
228
|
+
callback
|
|
229
|
+
);
|
|
208
230
|
} else {
|
|
209
|
-
find(
|
|
231
|
+
find(
|
|
232
|
+
roots,
|
|
233
|
+
extensions,
|
|
234
|
+
ignore,
|
|
235
|
+
includeSymlinks,
|
|
236
|
+
rootDir,
|
|
237
|
+
console,
|
|
238
|
+
callback
|
|
239
|
+
);
|
|
210
240
|
}
|
|
211
241
|
});
|
|
212
242
|
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
CanonicalPath,
|
|
14
|
+
Console,
|
|
14
15
|
CrawlerOptions,
|
|
15
16
|
FileData,
|
|
16
17
|
IgnoreMatcher,
|
|
@@ -33,6 +34,7 @@ function find(
|
|
|
33
34
|
ignore: IgnoreMatcher,
|
|
34
35
|
includeSymlinks: boolean,
|
|
35
36
|
rootDir: string,
|
|
37
|
+
console: Console,
|
|
36
38
|
callback: Callback,
|
|
37
39
|
): void {
|
|
38
40
|
const result: FileData = new Map();
|
|
@@ -44,51 +46,52 @@ function find(
|
|
|
44
46
|
fs.readdir(directory, {withFileTypes: true}, (err, entries) => {
|
|
45
47
|
activeCalls--;
|
|
46
48
|
if (err) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (entry.isSymbolicLink() && !includeSymlinks) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (entry.isDirectory()) {
|
|
63
|
-
search(file);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
49
|
+
console.warn(
|
|
50
|
+
`Error "${err.code ?? err.message}" reading contents of "${directory}", skipping. Add this directory to your ignore list to exclude it.`,
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
entries.forEach((entry: fs.Dirent) => {
|
|
54
|
+
const file = path.join(directory, entry.name.toString());
|
|
55
|
+
|
|
56
|
+
if (ignore(file)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
if (entry.isSymbolicLink() && !includeSymlinks) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
search(file);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
activeCalls++;
|
|
70
|
+
|
|
71
|
+
fs.lstat(file, (err, stat) => {
|
|
72
|
+
activeCalls--;
|
|
73
|
+
|
|
74
|
+
if (!err && stat) {
|
|
75
|
+
const ext = path.extname(file).substr(1);
|
|
76
|
+
if (stat.isSymbolicLink() || extensions.includes(ext)) {
|
|
77
|
+
result.set(pathUtils.absoluteToNormal(file), [
|
|
78
|
+
'',
|
|
79
|
+
stat.mtime.getTime(),
|
|
80
|
+
stat.size,
|
|
81
|
+
0,
|
|
82
|
+
'',
|
|
83
|
+
null,
|
|
84
|
+
stat.isSymbolicLink() ? 1 : 0,
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
84
87
|
}
|
|
85
|
-
}
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
if (activeCalls === 0) {
|
|
90
|
+
callback(result);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
90
93
|
});
|
|
91
|
-
}
|
|
94
|
+
}
|
|
92
95
|
|
|
93
96
|
if (activeCalls === 0) {
|
|
94
97
|
callback(result);
|
|
@@ -109,6 +112,7 @@ function findNative(
|
|
|
109
112
|
ignore: IgnoreMatcher,
|
|
110
113
|
includeSymlinks: boolean,
|
|
111
114
|
rootDir: string,
|
|
115
|
+
console: Console,
|
|
112
116
|
callback: Callback,
|
|
113
117
|
): void {
|
|
114
118
|
// Examples:
|
|
@@ -172,6 +176,7 @@ module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
|
|
|
172
176
|
changedFiles: FileData,
|
|
173
177
|
}> {
|
|
174
178
|
const {
|
|
179
|
+
console,
|
|
175
180
|
previousState,
|
|
176
181
|
extensions,
|
|
177
182
|
forceNodeFilesystemAPI,
|
|
@@ -194,7 +199,7 @@ module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
|
|
|
194
199
|
debug('Using system find: %s', useNativeFind);
|
|
195
200
|
|
|
196
201
|
return new Promise((resolve, reject) => {
|
|
197
|
-
const callback =
|
|
202
|
+
const callback: Callback = fileData => {
|
|
198
203
|
const difference = previousState.fileSystem.getDifference(fileData);
|
|
199
204
|
|
|
200
205
|
perfLogger?.point('nodeCrawl_end');
|
|
@@ -209,9 +214,25 @@ module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
|
|
|
209
214
|
};
|
|
210
215
|
|
|
211
216
|
if (useNativeFind) {
|
|
212
|
-
findNative(
|
|
217
|
+
findNative(
|
|
218
|
+
roots,
|
|
219
|
+
extensions,
|
|
220
|
+
ignore,
|
|
221
|
+
includeSymlinks,
|
|
222
|
+
rootDir,
|
|
223
|
+
console,
|
|
224
|
+
callback,
|
|
225
|
+
);
|
|
213
226
|
} else {
|
|
214
|
-
find(
|
|
227
|
+
find(
|
|
228
|
+
roots,
|
|
229
|
+
extensions,
|
|
230
|
+
ignore,
|
|
231
|
+
includeSymlinks,
|
|
232
|
+
rootDir,
|
|
233
|
+
console,
|
|
234
|
+
callback,
|
|
235
|
+
);
|
|
215
236
|
}
|
|
216
237
|
});
|
|
217
238
|
};
|
package/src/flow-types.js.flow
CHANGED
package/src/lib/RootPathUtils.js
CHANGED
|
@@ -45,8 +45,9 @@ function _interopRequireWildcard(obj, nodeInterop) {
|
|
|
45
45
|
}
|
|
46
46
|
return newObj;
|
|
47
47
|
}
|
|
48
|
-
const
|
|
49
|
-
const
|
|
48
|
+
const UP_FRAGMENT_SEP = ".." + path.sep;
|
|
49
|
+
const SEP_UP_FRAGMENT = path.sep + "..";
|
|
50
|
+
const UP_FRAGMENT_SEP_LENGTH = UP_FRAGMENT_SEP.length;
|
|
50
51
|
const CURRENT_FRAGMENT = "." + path.sep;
|
|
51
52
|
class RootPathUtils {
|
|
52
53
|
#rootDir;
|
|
@@ -70,6 +71,9 @@ class RootPathUtils {
|
|
|
70
71
|
this.#rootParts.pop();
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
getBasenameOfNthAncestor(n) {
|
|
75
|
+
return this.#rootParts[this.#rootParts.length - 1 - n];
|
|
76
|
+
}
|
|
73
77
|
absoluteToNormal(absolutePath) {
|
|
74
78
|
let endOfMatchingPrefix = 0;
|
|
75
79
|
let lastMatchingPartIdx = 0;
|
|
@@ -100,11 +104,11 @@ class RootPathUtils {
|
|
|
100
104
|
let i = 0;
|
|
101
105
|
let pos = 0;
|
|
102
106
|
while (
|
|
103
|
-
normalPath.startsWith(
|
|
107
|
+
normalPath.startsWith(UP_FRAGMENT_SEP, pos) ||
|
|
104
108
|
(normalPath.endsWith("..") && normalPath.length === 2 + pos)
|
|
105
109
|
) {
|
|
106
110
|
left = this.#rootDirnames[i === this.#rootDepth ? this.#rootDepth : ++i];
|
|
107
|
-
pos +=
|
|
111
|
+
pos += UP_FRAGMENT_SEP_LENGTH;
|
|
108
112
|
}
|
|
109
113
|
const right = pos === 0 ? normalPath : normalPath.slice(pos);
|
|
110
114
|
if (right.length === 0) {
|
|
@@ -121,13 +125,25 @@ class RootPathUtils {
|
|
|
121
125
|
path.relative(this.#rootDir, path.join(this.#rootDir, relativePath))
|
|
122
126
|
);
|
|
123
127
|
}
|
|
128
|
+
joinNormalToRelative(normalPath, relativePath) {
|
|
129
|
+
if (normalPath === "") {
|
|
130
|
+
return relativePath;
|
|
131
|
+
}
|
|
132
|
+
if (relativePath === "") {
|
|
133
|
+
return normalPath;
|
|
134
|
+
}
|
|
135
|
+
if (normalPath === ".." || normalPath.endsWith(SEP_UP_FRAGMENT)) {
|
|
136
|
+
return this.relativeToNormal(normalPath + path.sep + relativePath);
|
|
137
|
+
}
|
|
138
|
+
return normalPath + path.sep + relativePath;
|
|
139
|
+
}
|
|
124
140
|
#tryCollapseIndirectionsInSuffix(
|
|
125
141
|
fullPath,
|
|
126
142
|
startOfRelativePart,
|
|
127
143
|
implicitUpIndirections
|
|
128
144
|
) {
|
|
129
145
|
let totalUpIndirections = implicitUpIndirections;
|
|
130
|
-
for (let pos = startOfRelativePart; ; pos +=
|
|
146
|
+
for (let pos = startOfRelativePart; ; pos += UP_FRAGMENT_SEP_LENGTH) {
|
|
131
147
|
const nextIndirection = fullPath.indexOf(CURRENT_FRAGMENT, pos);
|
|
132
148
|
if (nextIndirection === -1) {
|
|
133
149
|
while (totalUpIndirections > 0) {
|
|
@@ -149,12 +165,12 @@ class RootPathUtils {
|
|
|
149
165
|
right === "" ||
|
|
150
166
|
(right === ".." && totalUpIndirections >= this.#rootParts.length - 1)
|
|
151
167
|
) {
|
|
152
|
-
return
|
|
168
|
+
return UP_FRAGMENT_SEP.repeat(totalUpIndirections).slice(0, -1);
|
|
153
169
|
}
|
|
154
170
|
if (totalUpIndirections === 0) {
|
|
155
171
|
return right;
|
|
156
172
|
}
|
|
157
|
-
return
|
|
173
|
+
return UP_FRAGMENT_SEP.repeat(totalUpIndirections) + right;
|
|
158
174
|
}
|
|
159
175
|
if (totalUpIndirections < this.#rootParts.length - 1) {
|
|
160
176
|
totalUpIndirections++;
|
|
@@ -34,8 +34,9 @@ import * as path from 'path';
|
|
|
34
34
|
* back to `node:path` equivalents in those cases.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
const
|
|
37
|
+
const UP_FRAGMENT_SEP = '..' + path.sep;
|
|
38
|
+
const SEP_UP_FRAGMENT = path.sep + '..';
|
|
39
|
+
const UP_FRAGMENT_SEP_LENGTH = UP_FRAGMENT_SEP.length;
|
|
39
40
|
const CURRENT_FRAGMENT = '.' + path.sep;
|
|
40
41
|
|
|
41
42
|
export class RootPathUtils {
|
|
@@ -66,6 +67,10 @@ export class RootPathUtils {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
getBasenameOfNthAncestor(n: number): string {
|
|
71
|
+
return this.#rootParts[this.#rootParts.length - 1 - n];
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
// absolutePath may be any well-formed absolute path.
|
|
70
75
|
absoluteToNormal(absolutePath: string): string {
|
|
71
76
|
let endOfMatchingPrefix = 0;
|
|
@@ -114,11 +119,11 @@ export class RootPathUtils {
|
|
|
114
119
|
let i = 0;
|
|
115
120
|
let pos = 0;
|
|
116
121
|
while (
|
|
117
|
-
normalPath.startsWith(
|
|
122
|
+
normalPath.startsWith(UP_FRAGMENT_SEP, pos) ||
|
|
118
123
|
(normalPath.endsWith('..') && normalPath.length === 2 + pos)
|
|
119
124
|
) {
|
|
120
125
|
left = this.#rootDirnames[i === this.#rootDepth ? this.#rootDepth : ++i];
|
|
121
|
-
pos +=
|
|
126
|
+
pos += UP_FRAGMENT_SEP_LENGTH;
|
|
122
127
|
}
|
|
123
128
|
const right = pos === 0 ? normalPath : normalPath.slice(pos);
|
|
124
129
|
if (right.length === 0) {
|
|
@@ -139,6 +144,22 @@ export class RootPathUtils {
|
|
|
139
144
|
);
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
// Takes a normal and relative path, and joins them efficiently into a normal
|
|
148
|
+
// path, including collapsing trailing '..' in the first part with leading
|
|
149
|
+
// project root segments in the relative part.
|
|
150
|
+
joinNormalToRelative(normalPath: string, relativePath: string): string {
|
|
151
|
+
if (normalPath === '') {
|
|
152
|
+
return relativePath;
|
|
153
|
+
}
|
|
154
|
+
if (relativePath === '') {
|
|
155
|
+
return normalPath;
|
|
156
|
+
}
|
|
157
|
+
if (normalPath === '..' || normalPath.endsWith(SEP_UP_FRAGMENT)) {
|
|
158
|
+
return this.relativeToNormal(normalPath + path.sep + relativePath);
|
|
159
|
+
}
|
|
160
|
+
return normalPath + path.sep + relativePath;
|
|
161
|
+
}
|
|
162
|
+
|
|
142
163
|
// Internal: Tries to collapse sequences like `../root/foo` for root
|
|
143
164
|
// `/project/root` down to the normal 'foo'.
|
|
144
165
|
#tryCollapseIndirectionsInSuffix(
|
|
@@ -151,7 +172,7 @@ export class RootPathUtils {
|
|
|
151
172
|
// unmatched suffix e.g /project/[../../foo], but bail out to Node's
|
|
152
173
|
// path.relative if we find a possible indirection after any later segment,
|
|
153
174
|
// or on any "./" that isn't a "../".
|
|
154
|
-
for (let pos = startOfRelativePart; ; pos +=
|
|
175
|
+
for (let pos = startOfRelativePart; ; pos += UP_FRAGMENT_SEP_LENGTH) {
|
|
155
176
|
const nextIndirection = fullPath.indexOf(CURRENT_FRAGMENT, pos);
|
|
156
177
|
if (nextIndirection === -1) {
|
|
157
178
|
// If we have any indirections, they may "collapse" if a subsequent
|
|
@@ -185,13 +206,13 @@ export class RootPathUtils {
|
|
|
185
206
|
) {
|
|
186
207
|
// If we have no right side (or an indirection that would take us
|
|
187
208
|
// below the root), just ensure we don't include a trailing separtor.
|
|
188
|
-
return
|
|
209
|
+
return UP_FRAGMENT_SEP.repeat(totalUpIndirections).slice(0, -1);
|
|
189
210
|
}
|
|
190
211
|
// Optimisation for the common case, saves a concatenation.
|
|
191
212
|
if (totalUpIndirections === 0) {
|
|
192
213
|
return right;
|
|
193
214
|
}
|
|
194
|
-
return
|
|
215
|
+
return UP_FRAGMENT_SEP.repeat(totalUpIndirections) + right;
|
|
195
216
|
}
|
|
196
217
|
|
|
197
218
|
// Cap the number of indirections at the total number of root segments.
|
package/src/lib/TreeFS.js
CHANGED
|
@@ -105,22 +105,20 @@ class TreeFS {
|
|
|
105
105
|
}
|
|
106
106
|
lookup(mixedPath) {
|
|
107
107
|
const normalPath = this._normalizePath(mixedPath);
|
|
108
|
+
const links = new Set();
|
|
108
109
|
const result = this._lookupByNormalPath(normalPath, {
|
|
110
|
+
collectLinkPaths: links,
|
|
109
111
|
followLeaf: true,
|
|
110
112
|
});
|
|
111
113
|
if (!result.exists) {
|
|
112
|
-
const { canonicalMissingPath
|
|
114
|
+
const { canonicalMissingPath } = result;
|
|
113
115
|
return {
|
|
114
116
|
exists: false,
|
|
115
|
-
links
|
|
116
|
-
canonicalLinkPaths.map((canonicalPath) =>
|
|
117
|
-
this.#pathUtils.normalToAbsolute(canonicalPath)
|
|
118
|
-
)
|
|
119
|
-
),
|
|
117
|
+
links,
|
|
120
118
|
missing: this.#pathUtils.normalToAbsolute(canonicalMissingPath),
|
|
121
119
|
};
|
|
122
120
|
}
|
|
123
|
-
const { canonicalPath,
|
|
121
|
+
const { canonicalPath, node } = result;
|
|
124
122
|
const type =
|
|
125
123
|
node instanceof Map
|
|
126
124
|
? "d"
|
|
@@ -135,11 +133,7 @@ class TreeFS {
|
|
|
135
133
|
);
|
|
136
134
|
return {
|
|
137
135
|
exists: true,
|
|
138
|
-
links
|
|
139
|
-
canonicalLinkPaths.map((canonicalPath) =>
|
|
140
|
-
this.#pathUtils.normalToAbsolute(canonicalPath)
|
|
141
|
-
)
|
|
142
|
-
),
|
|
136
|
+
links,
|
|
143
137
|
realPath: this.#pathUtils.normalToAbsolute(canonicalPath),
|
|
144
138
|
type,
|
|
145
139
|
};
|
|
@@ -180,8 +174,12 @@ class TreeFS {
|
|
|
180
174
|
if (!contextRootResult.exists) {
|
|
181
175
|
return;
|
|
182
176
|
}
|
|
183
|
-
const {
|
|
184
|
-
|
|
177
|
+
const {
|
|
178
|
+
ancestorOfRootIdx,
|
|
179
|
+
canonicalPath: rootRealPath,
|
|
180
|
+
node: contextRoot,
|
|
181
|
+
parentNode: contextRootParent,
|
|
182
|
+
} = contextRootResult;
|
|
185
183
|
if (!(contextRoot instanceof Map)) {
|
|
186
184
|
return;
|
|
187
185
|
}
|
|
@@ -194,13 +192,18 @@ class TreeFS {
|
|
|
194
192
|
filterComparePosix && _path.default.sep !== "/"
|
|
195
193
|
? contextRootAbsolutePath.replaceAll(_path.default.sep, "/")
|
|
196
194
|
: contextRootAbsolutePath;
|
|
197
|
-
for (const relativePathForComparison of this._pathIterator(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
195
|
+
for (const relativePathForComparison of this._pathIterator(
|
|
196
|
+
contextRoot,
|
|
197
|
+
contextRootParent,
|
|
198
|
+
ancestorOfRootIdx,
|
|
199
|
+
{
|
|
200
|
+
alwaysYieldPosix: filterComparePosix,
|
|
201
|
+
canonicalPathOfRoot: rootRealPath,
|
|
202
|
+
follow,
|
|
203
|
+
recursive,
|
|
204
|
+
subtreeOnly: rootDir != null,
|
|
205
|
+
}
|
|
206
|
+
)) {
|
|
204
207
|
if (
|
|
205
208
|
filter == null ||
|
|
206
209
|
filter.test(
|
|
@@ -295,10 +298,10 @@ class TreeFS {
|
|
|
295
298
|
}
|
|
296
299
|
) {
|
|
297
300
|
let targetNormalPath = requestedNormalPath;
|
|
298
|
-
const canonicalLinkPaths = [];
|
|
299
301
|
let seen;
|
|
300
302
|
let fromIdx = 0;
|
|
301
303
|
let parentNode = this.#rootNode;
|
|
304
|
+
let ancestorOfRootIdx = null;
|
|
302
305
|
while (targetNormalPath.length > fromIdx) {
|
|
303
306
|
const nextSepIdx = targetNormalPath.indexOf(_path.default.sep, fromIdx);
|
|
304
307
|
const isLastSegment = nextSepIdx === -1;
|
|
@@ -310,10 +313,15 @@ class TreeFS {
|
|
|
310
313
|
continue;
|
|
311
314
|
}
|
|
312
315
|
let segmentNode = parentNode.get(segmentName);
|
|
316
|
+
if (segmentName === "..") {
|
|
317
|
+
ancestorOfRootIdx =
|
|
318
|
+
ancestorOfRootIdx == null ? 1 : ancestorOfRootIdx + 1;
|
|
319
|
+
} else if (segmentNode != null) {
|
|
320
|
+
ancestorOfRootIdx = null;
|
|
321
|
+
}
|
|
313
322
|
if (segmentNode == null) {
|
|
314
323
|
if (opts.makeDirectories !== true && segmentName !== "..") {
|
|
315
324
|
return {
|
|
316
|
-
canonicalLinkPaths,
|
|
317
325
|
canonicalMissingPath: isLastSegment
|
|
318
326
|
? targetNormalPath
|
|
319
327
|
: targetNormalPath.slice(0, fromIdx - 1),
|
|
@@ -332,7 +340,7 @@ class TreeFS {
|
|
|
332
340
|
opts.followLeaf === false)
|
|
333
341
|
) {
|
|
334
342
|
return {
|
|
335
|
-
|
|
343
|
+
ancestorOfRootIdx,
|
|
336
344
|
canonicalPath: targetNormalPath,
|
|
337
345
|
exists: true,
|
|
338
346
|
node: segmentNode,
|
|
@@ -347,7 +355,6 @@ class TreeFS {
|
|
|
347
355
|
: targetNormalPath.slice(0, fromIdx - 1);
|
|
348
356
|
if (segmentNode[_constants.default.SYMLINK] === 0) {
|
|
349
357
|
return {
|
|
350
|
-
canonicalLinkPaths,
|
|
351
358
|
canonicalMissingPath: currentPath,
|
|
352
359
|
exists: false,
|
|
353
360
|
};
|
|
@@ -356,18 +363,22 @@ class TreeFS {
|
|
|
356
363
|
segmentNode,
|
|
357
364
|
currentPath
|
|
358
365
|
);
|
|
359
|
-
|
|
366
|
+
if (opts.collectLinkPaths) {
|
|
367
|
+
opts.collectLinkPaths.add(
|
|
368
|
+
this.#pathUtils.normalToAbsolute(currentPath)
|
|
369
|
+
);
|
|
370
|
+
}
|
|
360
371
|
targetNormalPath = isLastSegment
|
|
361
372
|
? normalSymlinkTarget
|
|
362
|
-
:
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
: this.#pathUtils.joinNormalToRelative(
|
|
374
|
+
normalSymlinkTarget,
|
|
375
|
+
targetNormalPath.slice(fromIdx)
|
|
376
|
+
);
|
|
365
377
|
if (seen == null) {
|
|
366
378
|
seen = new Set([requestedNormalPath]);
|
|
367
379
|
}
|
|
368
380
|
if (seen.has(targetNormalPath)) {
|
|
369
381
|
return {
|
|
370
|
-
canonicalLinkPaths,
|
|
371
382
|
canonicalMissingPath: targetNormalPath,
|
|
372
383
|
exists: false,
|
|
373
384
|
};
|
|
@@ -382,7 +393,7 @@ class TreeFS {
|
|
|
382
393
|
"Unexpectedly escaped traversal"
|
|
383
394
|
);
|
|
384
395
|
return {
|
|
385
|
-
|
|
396
|
+
ancestorOfRootIdx: null,
|
|
386
397
|
canonicalPath: targetNormalPath,
|
|
387
398
|
exists: true,
|
|
388
399
|
node: this.#rootNode,
|
|
@@ -422,10 +433,30 @@ class TreeFS {
|
|
|
422
433
|
? this.#pathUtils.absoluteToNormal(relativeOrAbsolutePath)
|
|
423
434
|
: this.#pathUtils.relativeToNormal(relativeOrAbsolutePath);
|
|
424
435
|
}
|
|
425
|
-
|
|
436
|
+
*#directoryNodeIterator(node, parent, ancestorOfRootIdx) {
|
|
437
|
+
if (ancestorOfRootIdx != null && parent) {
|
|
438
|
+
yield [
|
|
439
|
+
this.#pathUtils.getBasenameOfNthAncestor(ancestorOfRootIdx - 1),
|
|
440
|
+
parent,
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
yield* node.entries();
|
|
444
|
+
}
|
|
445
|
+
*_pathIterator(
|
|
446
|
+
iterationRootNode,
|
|
447
|
+
iterationRootParentNode,
|
|
448
|
+
ancestorOfRootIdx,
|
|
449
|
+
opts,
|
|
450
|
+
pathPrefix = "",
|
|
451
|
+
followedLinks = new Set()
|
|
452
|
+
) {
|
|
426
453
|
const pathSep = opts.alwaysYieldPosix ? "/" : _path.default.sep;
|
|
427
454
|
const prefixWithSep = pathPrefix === "" ? pathPrefix : pathPrefix + pathSep;
|
|
428
|
-
for (const [name, node] of
|
|
455
|
+
for (const [name, node] of this.#directoryNodeIterator(
|
|
456
|
+
iterationRootNode,
|
|
457
|
+
iterationRootParentNode,
|
|
458
|
+
ancestorOfRootIdx
|
|
459
|
+
)) {
|
|
429
460
|
if (opts.subtreeOnly && name === "..") {
|
|
430
461
|
continue;
|
|
431
462
|
}
|
|
@@ -458,6 +489,8 @@ class TreeFS {
|
|
|
458
489
|
) {
|
|
459
490
|
yield* this._pathIterator(
|
|
460
491
|
target,
|
|
492
|
+
resolved.parentNode,
|
|
493
|
+
resolved.ancestorOfRootIdx,
|
|
461
494
|
opts,
|
|
462
495
|
nodePath,
|
|
463
496
|
new Set([...followedLinks, node])
|
|
@@ -465,7 +498,16 @@ class TreeFS {
|
|
|
465
498
|
}
|
|
466
499
|
}
|
|
467
500
|
} else if (opts.recursive) {
|
|
468
|
-
yield* this._pathIterator(
|
|
501
|
+
yield* this._pathIterator(
|
|
502
|
+
node,
|
|
503
|
+
iterationRootParentNode,
|
|
504
|
+
ancestorOfRootIdx != null && ancestorOfRootIdx > 1
|
|
505
|
+
? ancestorOfRootIdx - 1
|
|
506
|
+
: null,
|
|
507
|
+
opts,
|
|
508
|
+
nodePath,
|
|
509
|
+
followedLinks
|
|
510
|
+
);
|
|
469
511
|
}
|
|
470
512
|
}
|
|
471
513
|
}
|
package/src/lib/TreeFS.js.flow
CHANGED
|
@@ -142,20 +142,20 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
142
142
|
|
|
143
143
|
lookup(mixedPath: Path): LookupResult {
|
|
144
144
|
const normalPath = this._normalizePath(mixedPath);
|
|
145
|
-
const
|
|
145
|
+
const links = new Set<string>();
|
|
146
|
+
const result = this._lookupByNormalPath(normalPath, {
|
|
147
|
+
collectLinkPaths: links,
|
|
148
|
+
followLeaf: true,
|
|
149
|
+
});
|
|
146
150
|
if (!result.exists) {
|
|
147
|
-
const {canonicalMissingPath
|
|
151
|
+
const {canonicalMissingPath} = result;
|
|
148
152
|
return {
|
|
149
153
|
exists: false,
|
|
150
|
-
links
|
|
151
|
-
canonicalLinkPaths.map(canonicalPath =>
|
|
152
|
-
this.#pathUtils.normalToAbsolute(canonicalPath),
|
|
153
|
-
),
|
|
154
|
-
),
|
|
154
|
+
links,
|
|
155
155
|
missing: this.#pathUtils.normalToAbsolute(canonicalMissingPath),
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
const {canonicalPath,
|
|
158
|
+
const {canonicalPath, node} = result;
|
|
159
159
|
const type = node instanceof Map ? 'd' : node[H.SYMLINK] === 0 ? 'f' : 'l';
|
|
160
160
|
invariant(
|
|
161
161
|
type !== 'l',
|
|
@@ -165,11 +165,7 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
165
165
|
);
|
|
166
166
|
return {
|
|
167
167
|
exists: true,
|
|
168
|
-
links
|
|
169
|
-
canonicalLinkPaths.map(canonicalPath =>
|
|
170
|
-
this.#pathUtils.normalToAbsolute(canonicalPath),
|
|
171
|
-
),
|
|
172
|
-
),
|
|
168
|
+
links,
|
|
173
169
|
realPath: this.#pathUtils.normalToAbsolute(canonicalPath),
|
|
174
170
|
type,
|
|
175
171
|
};
|
|
@@ -229,7 +225,12 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
229
225
|
if (!contextRootResult.exists) {
|
|
230
226
|
return;
|
|
231
227
|
}
|
|
232
|
-
const {
|
|
228
|
+
const {
|
|
229
|
+
ancestorOfRootIdx,
|
|
230
|
+
canonicalPath: rootRealPath,
|
|
231
|
+
node: contextRoot,
|
|
232
|
+
parentNode: contextRootParent,
|
|
233
|
+
} = contextRootResult;
|
|
233
234
|
if (!(contextRoot instanceof Map)) {
|
|
234
235
|
return;
|
|
235
236
|
}
|
|
@@ -245,13 +246,18 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
245
246
|
? contextRootAbsolutePath.replaceAll(path.sep, '/')
|
|
246
247
|
: contextRootAbsolutePath;
|
|
247
248
|
|
|
248
|
-
for (const relativePathForComparison of this._pathIterator(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
249
|
+
for (const relativePathForComparison of this._pathIterator(
|
|
250
|
+
contextRoot,
|
|
251
|
+
contextRootParent,
|
|
252
|
+
ancestorOfRootIdx,
|
|
253
|
+
{
|
|
254
|
+
alwaysYieldPosix: filterComparePosix,
|
|
255
|
+
canonicalPathOfRoot: rootRealPath,
|
|
256
|
+
follow,
|
|
257
|
+
recursive,
|
|
258
|
+
subtreeOnly: rootDir != null,
|
|
259
|
+
},
|
|
260
|
+
)) {
|
|
255
261
|
if (
|
|
256
262
|
filter == null ||
|
|
257
263
|
filter.test(
|
|
@@ -357,6 +363,9 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
357
363
|
_lookupByNormalPath(
|
|
358
364
|
requestedNormalPath: string,
|
|
359
365
|
opts: {
|
|
366
|
+
// Mutable Set into which absolute real paths of traversed symlinks will
|
|
367
|
+
// be added. Omit for performance if not needed.
|
|
368
|
+
collectLinkPaths?: ?Set<string>,
|
|
360
369
|
// Like lstat vs stat, whether to follow a symlink at the basename of
|
|
361
370
|
// the given path, or return the details of the symlink itself.
|
|
362
371
|
followLeaf?: boolean,
|
|
@@ -364,28 +373,25 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
364
373
|
} = {followLeaf: true, makeDirectories: false},
|
|
365
374
|
):
|
|
366
375
|
| {
|
|
367
|
-
|
|
376
|
+
ancestorOfRootIdx: ?number,
|
|
368
377
|
canonicalPath: string,
|
|
369
378
|
exists: true,
|
|
370
379
|
node: MixedNode,
|
|
371
380
|
parentNode: DirectoryNode,
|
|
372
381
|
}
|
|
373
382
|
| {
|
|
374
|
-
|
|
383
|
+
ancestorOfRootIdx: ?number,
|
|
375
384
|
canonicalPath: string,
|
|
376
385
|
exists: true,
|
|
377
386
|
node: DirectoryNode,
|
|
378
387
|
parentNode: null,
|
|
379
388
|
}
|
|
380
389
|
| {
|
|
381
|
-
canonicalLinkPaths: Array<string>,
|
|
382
390
|
canonicalMissingPath: string,
|
|
383
391
|
exists: false,
|
|
384
392
|
} {
|
|
385
393
|
// We'll update the target if we hit a symlink.
|
|
386
394
|
let targetNormalPath = requestedNormalPath;
|
|
387
|
-
// Set of traversed symlink paths to return.
|
|
388
|
-
const canonicalLinkPaths: Array<string> = [];
|
|
389
395
|
// Lazy-initialised set of seen target paths, to detect symlink cycles.
|
|
390
396
|
let seen: ?Set<string>;
|
|
391
397
|
// Pointer to the first character of the current path segment in
|
|
@@ -393,6 +399,9 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
393
399
|
let fromIdx = 0;
|
|
394
400
|
// The parent of the current segment
|
|
395
401
|
let parentNode = this.#rootNode;
|
|
402
|
+
// If a returned node is a strict ancestor of the root, this is the number
|
|
403
|
+
// of levels below the root, i.e. '..' is 1, '../..' is 2, otherwise null.
|
|
404
|
+
let ancestorOfRootIdx: ?number = null;
|
|
396
405
|
|
|
397
406
|
while (targetNormalPath.length > fromIdx) {
|
|
398
407
|
const nextSepIdx = targetNormalPath.indexOf(path.sep, fromIdx);
|
|
@@ -408,10 +417,18 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
408
417
|
|
|
409
418
|
let segmentNode = parentNode.get(segmentName);
|
|
410
419
|
|
|
420
|
+
// In normal paths all indirections are at the prefix, so we are at the
|
|
421
|
+
// nth ancestor of the root iff the path so far is n '..' segments.
|
|
422
|
+
if (segmentName === '..') {
|
|
423
|
+
ancestorOfRootIdx =
|
|
424
|
+
ancestorOfRootIdx == null ? 1 : ancestorOfRootIdx + 1;
|
|
425
|
+
} else if (segmentNode != null) {
|
|
426
|
+
ancestorOfRootIdx = null;
|
|
427
|
+
}
|
|
428
|
+
|
|
411
429
|
if (segmentNode == null) {
|
|
412
430
|
if (opts.makeDirectories !== true && segmentName !== '..') {
|
|
413
431
|
return {
|
|
414
|
-
canonicalLinkPaths,
|
|
415
432
|
canonicalMissingPath: isLastSegment
|
|
416
433
|
? targetNormalPath
|
|
417
434
|
: targetNormalPath.slice(0, fromIdx - 1),
|
|
@@ -433,7 +450,7 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
433
450
|
opts.followLeaf === false)
|
|
434
451
|
) {
|
|
435
452
|
return {
|
|
436
|
-
|
|
453
|
+
ancestorOfRootIdx,
|
|
437
454
|
canonicalPath: targetNormalPath,
|
|
438
455
|
exists: true,
|
|
439
456
|
node: segmentNode,
|
|
@@ -452,7 +469,6 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
452
469
|
if (segmentNode[H.SYMLINK] === 0) {
|
|
453
470
|
// Regular file in a directory path
|
|
454
471
|
return {
|
|
455
|
-
canonicalLinkPaths,
|
|
456
472
|
canonicalMissingPath: currentPath,
|
|
457
473
|
exists: false,
|
|
458
474
|
};
|
|
@@ -463,13 +479,21 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
463
479
|
segmentNode,
|
|
464
480
|
currentPath,
|
|
465
481
|
);
|
|
466
|
-
|
|
482
|
+
if (opts.collectLinkPaths) {
|
|
483
|
+
opts.collectLinkPaths.add(
|
|
484
|
+
this.#pathUtils.normalToAbsolute(currentPath),
|
|
485
|
+
);
|
|
486
|
+
}
|
|
467
487
|
|
|
468
488
|
// Append any subsequent path segments to the symlink target, and reset
|
|
469
489
|
// with our new target.
|
|
470
490
|
targetNormalPath = isLastSegment
|
|
471
491
|
? normalSymlinkTarget
|
|
472
|
-
:
|
|
492
|
+
: this.#pathUtils.joinNormalToRelative(
|
|
493
|
+
normalSymlinkTarget,
|
|
494
|
+
targetNormalPath.slice(fromIdx),
|
|
495
|
+
);
|
|
496
|
+
|
|
473
497
|
if (seen == null) {
|
|
474
498
|
// Optimisation: set this lazily only when we've encountered a symlink
|
|
475
499
|
seen = new Set([requestedNormalPath]);
|
|
@@ -477,7 +501,6 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
477
501
|
if (seen.has(targetNormalPath)) {
|
|
478
502
|
// TODO: Warn `Symlink cycle detected: ${[...seen, node].join(' -> ')}`
|
|
479
503
|
return {
|
|
480
|
-
canonicalLinkPaths,
|
|
481
504
|
canonicalMissingPath: targetNormalPath,
|
|
482
505
|
exists: false,
|
|
483
506
|
};
|
|
@@ -489,7 +512,7 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
489
512
|
}
|
|
490
513
|
invariant(parentNode === this.#rootNode, 'Unexpectedly escaped traversal');
|
|
491
514
|
return {
|
|
492
|
-
|
|
515
|
+
ancestorOfRootIdx: null,
|
|
493
516
|
canonicalPath: targetNormalPath,
|
|
494
517
|
exists: true,
|
|
495
518
|
node: this.#rootNode,
|
|
@@ -540,12 +563,28 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
540
563
|
: this.#pathUtils.relativeToNormal(relativeOrAbsolutePath);
|
|
541
564
|
}
|
|
542
565
|
|
|
566
|
+
*#directoryNodeIterator(
|
|
567
|
+
node: DirectoryNode,
|
|
568
|
+
parent: ?DirectoryNode,
|
|
569
|
+
ancestorOfRootIdx: ?number,
|
|
570
|
+
): Iterator<[string, MixedNode]> {
|
|
571
|
+
if (ancestorOfRootIdx != null && parent) {
|
|
572
|
+
yield [
|
|
573
|
+
this.#pathUtils.getBasenameOfNthAncestor(ancestorOfRootIdx - 1),
|
|
574
|
+
parent,
|
|
575
|
+
];
|
|
576
|
+
}
|
|
577
|
+
yield* node.entries();
|
|
578
|
+
}
|
|
579
|
+
|
|
543
580
|
/**
|
|
544
581
|
* Enumerate paths under a given node, including symlinks and through
|
|
545
582
|
* symlinks (if `follow` is enabled).
|
|
546
583
|
*/
|
|
547
584
|
*_pathIterator(
|
|
548
|
-
|
|
585
|
+
iterationRootNode: DirectoryNode,
|
|
586
|
+
iterationRootParentNode: ?DirectoryNode,
|
|
587
|
+
ancestorOfRootIdx: ?number,
|
|
549
588
|
opts: $ReadOnly<{
|
|
550
589
|
alwaysYieldPosix: boolean,
|
|
551
590
|
canonicalPathOfRoot: string,
|
|
@@ -558,7 +597,11 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
558
597
|
): Iterable<Path> {
|
|
559
598
|
const pathSep = opts.alwaysYieldPosix ? '/' : path.sep;
|
|
560
599
|
const prefixWithSep = pathPrefix === '' ? pathPrefix : pathPrefix + pathSep;
|
|
561
|
-
for (const [name, node] of
|
|
600
|
+
for (const [name, node] of this.#directoryNodeIterator(
|
|
601
|
+
iterationRootNode,
|
|
602
|
+
iterationRootParentNode,
|
|
603
|
+
ancestorOfRootIdx,
|
|
604
|
+
)) {
|
|
562
605
|
if (opts.subtreeOnly && name === '..') {
|
|
563
606
|
continue;
|
|
564
607
|
}
|
|
@@ -608,6 +651,8 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
608
651
|
// the path where we found the symlink as a prefix.
|
|
609
652
|
yield* this._pathIterator(
|
|
610
653
|
target,
|
|
654
|
+
resolved.parentNode,
|
|
655
|
+
resolved.ancestorOfRootIdx,
|
|
611
656
|
opts,
|
|
612
657
|
nodePath,
|
|
613
658
|
new Set([...followedLinks, node]),
|
|
@@ -615,7 +660,16 @@ export default class TreeFS implements MutableFileSystem {
|
|
|
615
660
|
}
|
|
616
661
|
}
|
|
617
662
|
} else if (opts.recursive) {
|
|
618
|
-
yield* this._pathIterator(
|
|
663
|
+
yield* this._pathIterator(
|
|
664
|
+
node,
|
|
665
|
+
iterationRootParentNode,
|
|
666
|
+
ancestorOfRootIdx != null && ancestorOfRootIdx > 1
|
|
667
|
+
? ancestorOfRootIdx - 1
|
|
668
|
+
: null,
|
|
669
|
+
opts,
|
|
670
|
+
nodePath,
|
|
671
|
+
followedLinks,
|
|
672
|
+
);
|
|
619
673
|
}
|
|
620
674
|
}
|
|
621
675
|
}
|
|
@@ -7,7 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
class RecrawlWarning {
|
|
8
8
|
static RECRAWL_WARNINGS = [];
|
|
9
9
|
static REGEXP =
|
|
10
|
-
/Recrawled this watch (\d+) times
|
|
10
|
+
/Recrawled this watch (\d+) times?, most recently because:\n([^:]+)/;
|
|
11
11
|
constructor(root, count) {
|
|
12
12
|
this.root = root;
|
|
13
13
|
this.count = count;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
export default class RecrawlWarning {
|
|
20
20
|
static RECRAWL_WARNINGS: Array<RecrawlWarning> = [];
|
|
21
21
|
static REGEXP: RegExp =
|
|
22
|
-
/Recrawled this watch (\d+) times
|
|
22
|
+
/Recrawled this watch (\d+) times?, most recently because:\n([^:]+)/;
|
|
23
23
|
|
|
24
24
|
root: string;
|
|
25
25
|
count: number;
|