reslop 0.2.5 → 0.2.7
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.md +22 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +72 -19
- package/lib/api.js +1 -1
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +61 -75
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +357 -0
- package/lib/dashboard/npm.js +290 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +79 -91
- package/lib/find.js +61 -0
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +148 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +161 -214
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +75 -83
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +412 -347
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +496 -0
- package/lib/render/dash-table.js +295 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +60 -86
- package/lib/render/frame.js +82 -117
- package/lib/render/notes.js +150 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/packages.js +93 -0
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +77 -172
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/repos.js +226 -0
- package/lib/render/search.js +25 -0
- package/lib/render/tiles.js +355 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +69 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +189 -65
- package/lib/session/branches.js +164 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +298 -352
- package/lib/session/compose.js +572 -715
- package/lib/session/dashboard.js +530 -0
- package/lib/session/dispatch.js +197 -171
- package/lib/session/files-pane.js +92 -45
- package/lib/session/find.js +94 -0
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +420 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +4 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/packages.js +143 -0
- package/lib/session/pointer.js +30 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +277 -236
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +59 -80
- package/lib/session/watch.js +201 -212
- package/lib/session/workspace.js +455 -0
- package/lib/session.js +95 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/workspace.js +166 -0
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/session/watch.js
CHANGED
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
|
|
6
|
+
const runs = require('../runs.js');
|
|
7
|
+
const { isRunsRel } = runs;
|
|
8
|
+
|
|
6
9
|
const DEBOUNCE_MS = 200;
|
|
10
|
+
const UNKNOWN_PATH = '.';
|
|
11
|
+
const MODULES_STAMP = 'node_modules/.package-lock.json';
|
|
7
12
|
|
|
8
13
|
const SKIP_DIRS = ['node_modules', '.review', 'coverage', 'dist', '.cache'];
|
|
9
14
|
|
|
@@ -14,41 +19,30 @@ const GIT_WATCH_NAMES = [
|
|
|
14
19
|
'FETCH_HEAD',
|
|
15
20
|
'ORIG_HEAD',
|
|
16
21
|
'refs',
|
|
22
|
+
'rebase-merge',
|
|
23
|
+
'rebase-apply',
|
|
17
24
|
];
|
|
18
25
|
|
|
19
|
-
const linuxRecursiveNode = (version) => {
|
|
20
|
-
const parts = `${version}`.split('.');
|
|
21
|
-
const major = Number(parts[0]);
|
|
22
|
-
const minor = Number(parts[1]);
|
|
23
|
-
if (major > 19) return true;
|
|
24
|
-
return major === 19 && minor >= 1;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
26
|
const canWatchRecursive = (
|
|
28
27
|
platform = process.platform,
|
|
29
28
|
version = process.versions.node,
|
|
30
29
|
) => {
|
|
31
30
|
if (platform === 'win32' || platform === 'darwin') return true;
|
|
32
|
-
if (platform
|
|
33
|
-
|
|
31
|
+
if (platform !== 'linux') return false;
|
|
32
|
+
const [major, minor] = `${version}`.split('.').map(Number);
|
|
33
|
+
return major > 19 || (major === 19 && minor >= 1);
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
if (!name) return false;
|
|
38
|
-
if (name.endsWith('.lock')) return true;
|
|
39
|
-
if (name.endsWith('~')) return true;
|
|
40
|
-
if (name.endsWith('.swp') || name.endsWith('.swo')) return true;
|
|
41
|
-
if (name.startsWith('.#')) return true;
|
|
42
|
-
if (name === '.DS_Store') return true;
|
|
43
|
-
return false;
|
|
44
|
-
};
|
|
36
|
+
const IGNORED_SUFFIXES = ['.lock', '~', '.swp', '.swo'];
|
|
45
37
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
const ignoredName = (name) =>
|
|
39
|
+
IGNORED_SUFFIXES.some((suffix) => name.endsWith(suffix)) ||
|
|
40
|
+
name.startsWith('.#') ||
|
|
41
|
+
name === '.DS_Store';
|
|
42
|
+
|
|
43
|
+
const ignoredGitRel = ([, name = '', sub]) => {
|
|
44
|
+
if (name === 'logs' && sub === 'HEAD') return false;
|
|
45
|
+
return name !== '' && !GIT_WATCH_NAMES.includes(name);
|
|
52
46
|
};
|
|
53
47
|
|
|
54
48
|
const slashRel = (rel) => `${rel}`.replaceAll('\\', '/');
|
|
@@ -56,34 +50,24 @@ const slashRel = (rel) => `${rel}`.replaceAll('\\', '/');
|
|
|
56
50
|
const ignoredRel = (rel) => {
|
|
57
51
|
const norm = slashRel(rel);
|
|
58
52
|
if (!norm || norm === '.') return false;
|
|
53
|
+
if (norm === MODULES_STAMP) return false;
|
|
59
54
|
const parts = norm.split('/');
|
|
60
|
-
|
|
61
|
-
if (SKIP_DIRS.includes(part)) return true;
|
|
62
|
-
}
|
|
55
|
+
if (parts.some((part) => SKIP_DIRS.includes(part))) return true;
|
|
63
56
|
if (parts[0] === '.git') return ignoredGitRel(parts);
|
|
64
57
|
return ignoredName(parts[parts.length - 1]);
|
|
65
58
|
};
|
|
66
59
|
|
|
67
60
|
const skipDirName = (name) => name === '.git' || SKIP_DIRS.includes(name);
|
|
68
61
|
|
|
69
|
-
const closeWatcher = (watcher) => {
|
|
70
|
-
try {
|
|
71
|
-
watcher.close();
|
|
72
|
-
} catch {
|
|
73
|
-
// already closed
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
62
|
const reviewMarkdown = (rel) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const rest = norm.slice('.review/'.length);
|
|
63
|
+
if (!rel.startsWith('.review/')) return false;
|
|
64
|
+
const rest = rel.slice('.review/'.length);
|
|
81
65
|
if (!rest || rest.includes('/')) return false;
|
|
82
66
|
return rest.endsWith('.md');
|
|
83
67
|
};
|
|
84
68
|
|
|
85
69
|
const isReviewDir = (root, dir) => {
|
|
86
|
-
const rel = path.relative(root, dir)
|
|
70
|
+
const rel = slashRel(path.relative(root, dir));
|
|
87
71
|
return rel === '.review' || rel.startsWith('.review/');
|
|
88
72
|
};
|
|
89
73
|
|
|
@@ -106,16 +90,15 @@ const isBareReviewName = (root, dir, rel) => {
|
|
|
106
90
|
const isReviewEvent = (root, dir, name) => {
|
|
107
91
|
if (isReviewDir(root, dir)) return true;
|
|
108
92
|
if (!name) return false;
|
|
109
|
-
const rel = path.relative(root, path.join(dir, name));
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return isBareReviewName(root, dir, norm);
|
|
93
|
+
const rel = slashRel(path.relative(root, path.join(dir, name)));
|
|
94
|
+
if (reviewMarkdown(rel)) return true;
|
|
95
|
+
return isBareReviewName(root, dir, rel);
|
|
113
96
|
};
|
|
114
97
|
|
|
115
98
|
const isOwnDirEvent = (dir, name) => {
|
|
116
99
|
if (!name) return false;
|
|
117
100
|
const base = path.basename(dir);
|
|
118
|
-
const slash =
|
|
101
|
+
const slash = slashRel(name);
|
|
119
102
|
const bare = slash.startsWith('./') ? slash.slice(2) : slash;
|
|
120
103
|
const trimmed = bare.endsWith('/') ? bare.slice(0, -1) : bare;
|
|
121
104
|
if (trimmed === base) {
|
|
@@ -144,200 +127,206 @@ const unchangedSince = (full, since) => {
|
|
|
144
127
|
}
|
|
145
128
|
};
|
|
146
129
|
|
|
147
|
-
const schedule = (state, timerKey, fire) => {
|
|
148
|
-
if (state.closed || !fire) return;
|
|
149
|
-
if (state[timerKey] !== null) clearTimeout(state[timerKey]);
|
|
150
|
-
state[timerKey] = setTimeout(() => {
|
|
151
|
-
state[timerKey] = null;
|
|
152
|
-
if (state.closed) return;
|
|
153
|
-
fire();
|
|
154
|
-
}, state.debounceMs);
|
|
155
|
-
const timer = state[timerKey];
|
|
156
|
-
if (timer && typeof timer.unref === 'function') timer.unref();
|
|
157
|
-
};
|
|
158
|
-
|
|
159
130
|
const REVIEW_HOLD_MS = 200;
|
|
160
131
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
132
|
+
class DiskWatcher {
|
|
133
|
+
constructor(options) {
|
|
134
|
+
this.root = path.resolve(options.root);
|
|
135
|
+
this.debounceMs = options.debounceMs ?? DEBOUNCE_MS;
|
|
136
|
+
this.onChange = options.onChange;
|
|
137
|
+
this.onReview = options.onReview;
|
|
138
|
+
this.onPaths = options.onPaths;
|
|
139
|
+
this.matchReview = options.matchReview ?? null;
|
|
140
|
+
this.recursive = options.recursive ?? canWatchRecursive();
|
|
141
|
+
this.watchers = [];
|
|
142
|
+
this.watched = new Set();
|
|
143
|
+
this.files = new Set();
|
|
144
|
+
this.pending = new Set();
|
|
145
|
+
this.timers = { change: null, review: null, loose: null, paths: null };
|
|
146
|
+
this.looseChange = false;
|
|
147
|
+
this.reviewSeenAt = 0;
|
|
148
|
+
this.startedAt = Date.now();
|
|
149
|
+
this.closed = false;
|
|
150
|
+
const scopes = options.paths?.length ? options.paths : ['.'];
|
|
151
|
+
for (const spec of scopes) this.watchScope(spec);
|
|
152
|
+
const gitDir = path.resolve(this.root, '.git');
|
|
153
|
+
this.watchOne(gitDir);
|
|
154
|
+
this.watchOne(path.join(gitDir, 'refs'));
|
|
155
|
+
this.watchOne(path.join(gitDir, 'logs'));
|
|
156
|
+
this.watchOne(path.resolve(this.root, runs.runsDir('.')));
|
|
157
|
+
this.watchOne(path.resolve(this.root, '.review'));
|
|
158
|
+
}
|
|
167
159
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
};
|
|
160
|
+
schedule(key, fire) {
|
|
161
|
+
if (this.closed || !fire) return;
|
|
162
|
+
this.clearTimer(key);
|
|
163
|
+
this.timers[key] = setTimeout(() => {
|
|
164
|
+
this.timers[key] = null;
|
|
165
|
+
if (!this.closed) fire();
|
|
166
|
+
}, this.debounceMs);
|
|
167
|
+
this.timers[key].unref();
|
|
168
|
+
}
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
});
|
|
181
|
-
};
|
|
170
|
+
clearTimer(key) {
|
|
171
|
+
if (this.timers[key] !== null) clearTimeout(this.timers[key]);
|
|
172
|
+
this.timers[key] = null;
|
|
173
|
+
}
|
|
182
174
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
reviewBurst() {
|
|
176
|
+
if (!this.reviewSeenAt) return false;
|
|
177
|
+
const hold = Math.max(this.debounceMs * 2, REVIEW_HOLD_MS);
|
|
178
|
+
return Date.now() - this.reviewSeenAt < hold;
|
|
179
|
+
}
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
181
|
+
flushPaths() {
|
|
182
|
+
if (!this.pending.size) return;
|
|
183
|
+
const list = [...this.pending];
|
|
184
|
+
this.pending.clear();
|
|
185
|
+
if (this.onPaths) this.onPaths(list);
|
|
186
|
+
}
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
state.reviewSeenAt = Date.now();
|
|
202
|
-
dropLooseChange(state);
|
|
203
|
-
schedule(state, 'reviewTimer', state.onReview);
|
|
204
|
-
if (!wanted) return;
|
|
188
|
+
scheduleChange(loose = false) {
|
|
189
|
+
const idle = this.timers.change === null;
|
|
190
|
+
this.looseChange = loose && (idle || this.looseChange);
|
|
191
|
+
this.schedule('change', () => {
|
|
192
|
+
if (this.looseChange && this.reviewBurst()) return;
|
|
193
|
+
this.flushPaths();
|
|
194
|
+
if (this.onChange) this.onChange();
|
|
195
|
+
});
|
|
205
196
|
}
|
|
206
|
-
if (!name) return void scheduleUnnamed(state);
|
|
207
|
-
if (!wanted && ignoredRel(rel)) return;
|
|
208
|
-
if (isOwnDirEvent(dir, name)) return;
|
|
209
|
-
const full = path.join(dir, name);
|
|
210
|
-
if (unchangedSince(full, state.startedAt)) return;
|
|
211
|
-
if (eventType === 'rename') state.watchNewDir(full);
|
|
212
|
-
scheduleChange(state);
|
|
213
|
-
};
|
|
214
197
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
};
|
|
222
|
-
const watcher = fs.watch(
|
|
223
|
-
target,
|
|
224
|
-
{ persistent: false, encoding: 'utf8', ...extra },
|
|
225
|
-
onEvent,
|
|
226
|
-
);
|
|
227
|
-
if (typeof watcher.on === 'function') {
|
|
228
|
-
watcher.on('error', () => {});
|
|
229
|
-
}
|
|
230
|
-
state.watched.add(target);
|
|
231
|
-
state.watchers.push(watcher);
|
|
232
|
-
} catch {
|
|
233
|
-
// missing path or too many watchers
|
|
198
|
+
scheduleUnnamed() {
|
|
199
|
+
if (this.reviewBurst()) return;
|
|
200
|
+
this.pending.add(UNKNOWN_PATH);
|
|
201
|
+
this.schedule('loose', () => {
|
|
202
|
+
if (!this.reviewBurst()) this.scheduleChange(true);
|
|
203
|
+
});
|
|
234
204
|
}
|
|
235
|
-
};
|
|
236
205
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
let entries;
|
|
241
|
-
try {
|
|
242
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
243
|
-
} catch {
|
|
244
|
-
return;
|
|
206
|
+
dropLooseChange() {
|
|
207
|
+
this.clearTimer('loose');
|
|
208
|
+
if (this.looseChange) this.clearTimer('change');
|
|
245
209
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
|
|
210
|
+
|
|
211
|
+
onEvent(dir, eventType, filename) {
|
|
212
|
+
if (this.closed) return;
|
|
213
|
+
const name = filename ? `${filename}` : '';
|
|
214
|
+
const rel = name
|
|
215
|
+
? slashRel(path.relative(this.root, path.join(dir, name)))
|
|
216
|
+
: '';
|
|
217
|
+
const wanted = this.files.has(rel);
|
|
218
|
+
if (isRunsRel(rel)) {
|
|
219
|
+
this.pending.add(rel);
|
|
220
|
+
if (name && eventType === 'rename') {
|
|
221
|
+
this.watchNewDir(path.join(dir, name));
|
|
222
|
+
}
|
|
223
|
+
return void this.schedule('paths', () => this.flushPaths());
|
|
224
|
+
}
|
|
225
|
+
if (isReviewEvent(this.root, dir, name)) {
|
|
226
|
+
this.reviewSeenAt = Date.now();
|
|
227
|
+
this.dropLooseChange();
|
|
228
|
+
this.schedule('review', this.onReview);
|
|
229
|
+
if (!wanted) return;
|
|
230
|
+
}
|
|
231
|
+
if (rel && this.matchReview && this.matchReview(rel)) {
|
|
232
|
+
this.pending.add(rel);
|
|
233
|
+
return void this.schedule('paths', () => this.flushPaths());
|
|
234
|
+
}
|
|
235
|
+
if (!name) return void this.scheduleUnnamed();
|
|
236
|
+
if (!wanted && ignoredRel(rel)) return;
|
|
237
|
+
if (isOwnDirEvent(dir, name)) return;
|
|
238
|
+
const full = path.join(dir, name);
|
|
239
|
+
if (unchangedSince(full, this.startedAt)) return;
|
|
240
|
+
if (eventType === 'rename') this.watchNewDir(full);
|
|
241
|
+
this.pending.add(rel);
|
|
242
|
+
this.scheduleChange();
|
|
250
243
|
}
|
|
251
|
-
};
|
|
252
244
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
245
|
+
watchOne(target, recursive = false) {
|
|
246
|
+
if (this.closed || this.watched.has(target)) return;
|
|
247
|
+
try {
|
|
248
|
+
const options = { persistent: false, encoding: 'utf8', recursive };
|
|
249
|
+
const watcher = fs.watch(target, options, (eventType, filename) => {
|
|
250
|
+
this.onEvent(target, eventType, filename);
|
|
251
|
+
});
|
|
252
|
+
watcher.on('error', () => {});
|
|
253
|
+
this.watched.add(target);
|
|
254
|
+
this.watchers.push(watcher);
|
|
255
|
+
} catch {
|
|
256
|
+
// missing path or too many watchers
|
|
257
|
+
}
|
|
260
258
|
}
|
|
261
|
-
if (!stat.isDirectory()) return;
|
|
262
|
-
if (path.basename(full) === '.review') return void watchOne(state, full);
|
|
263
|
-
if (skipDirName(path.basename(full))) return;
|
|
264
|
-
watchTree(state, full);
|
|
265
|
-
};
|
|
266
259
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
260
|
+
watchTree(dir) {
|
|
261
|
+
this.watchOne(dir, this.recursive);
|
|
262
|
+
if (this.recursive) return;
|
|
263
|
+
let entries;
|
|
264
|
+
try {
|
|
265
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
266
|
+
} catch {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
for (const entry of entries) {
|
|
270
|
+
if (!entry.isDirectory() || skipDirName(entry.name)) continue;
|
|
271
|
+
this.watchTree(path.join(dir, entry.name));
|
|
272
|
+
}
|
|
274
273
|
}
|
|
275
|
-
if (stat.isDirectory()) return void watchTree(state, target);
|
|
276
|
-
watchOne(state, path.dirname(target));
|
|
277
|
-
};
|
|
278
274
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
275
|
+
watchNewDir(full) {
|
|
276
|
+
if (this.recursive) return;
|
|
277
|
+
try {
|
|
278
|
+
if (!fs.statSync(full).isDirectory()) return;
|
|
279
|
+
} catch {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const name = path.basename(full);
|
|
283
|
+
if (name === '.review') this.watchOne(full);
|
|
284
|
+
else if (!skipDirName(name)) this.watchTree(full);
|
|
285
|
+
}
|
|
285
286
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
287
|
+
watchScope(spec) {
|
|
288
|
+
const target = path.resolve(this.root, spec);
|
|
289
|
+
try {
|
|
290
|
+
if (fs.statSync(target).isDirectory()) return void this.watchTree(target);
|
|
291
|
+
} catch {
|
|
292
|
+
// watch the parent until the path appears
|
|
293
|
+
}
|
|
294
|
+
this.watchOne(path.dirname(target));
|
|
295
|
+
}
|
|
294
296
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
watchFiles(rels) {
|
|
298
|
+
this.files = new Set();
|
|
299
|
+
for (const rel of rels) {
|
|
300
|
+
if (!rel) continue;
|
|
301
|
+
const full = path.resolve(this.root, rel);
|
|
302
|
+
this.files.add(slashRel(path.relative(this.root, full)));
|
|
303
|
+
this.watchOne(path.dirname(full));
|
|
304
|
+
}
|
|
302
305
|
}
|
|
303
|
-
};
|
|
304
306
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
looseChange: false,
|
|
321
|
-
reviewSeenAt: 0,
|
|
322
|
-
startedAt: Date.now(),
|
|
323
|
-
closed: false,
|
|
324
|
-
watchNewDir: (full) => maybeWatchNewDir(state, full),
|
|
325
|
-
};
|
|
326
|
-
const scopes = options.paths && options.paths.length ? options.paths : ['.'];
|
|
327
|
-
for (const spec of scopes) watchScope(state, spec);
|
|
328
|
-
watchGit(state);
|
|
329
|
-
watchOne(state, path.resolve(state.root, '.review'));
|
|
330
|
-
return {
|
|
331
|
-
close: () => stopState(state),
|
|
332
|
-
watchFiles: (rels) => watchFiles(state, rels),
|
|
333
|
-
};
|
|
334
|
-
};
|
|
307
|
+
close() {
|
|
308
|
+
this.closed = true;
|
|
309
|
+
for (const key of Object.keys(this.timers)) this.clearTimer(key);
|
|
310
|
+
this.pending.clear();
|
|
311
|
+
for (const watcher of this.watchers) {
|
|
312
|
+
try {
|
|
313
|
+
watcher.close();
|
|
314
|
+
} catch {
|
|
315
|
+
// already closed
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
this.watchers = [];
|
|
319
|
+
this.watched.clear();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
335
322
|
|
|
336
323
|
module.exports = {
|
|
337
324
|
DEBOUNCE_MS,
|
|
325
|
+
UNKNOWN_PATH,
|
|
326
|
+
MODULES_STAMP,
|
|
338
327
|
canWatchRecursive,
|
|
339
328
|
ignoredRel,
|
|
340
329
|
isOwnDirEvent,
|
|
341
330
|
unchangedSince,
|
|
342
|
-
|
|
331
|
+
DiskWatcher,
|
|
343
332
|
};
|