reslop 0.2.4 → 0.2.6
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 +25 -1
- package/LICENSE +1 -1
- package/README.md +12 -13
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -12
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +48 -18
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +196 -404
- package/lib/deps.js +240 -399
- 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 +138 -141
- 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 +90 -206
- package/lib/editor.js +98 -55
- package/lib/files.js +86 -103
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +182 -344
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- 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 +157 -132
- 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 +16 -26
- 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 +22 -35
- package/lib/highlight/txt.js +9 -22
- package/lib/highlight.js +17 -46
- package/lib/keys.js +62 -85
- package/lib/manifest.js +67 -130
- package/lib/npm-commands.js +119 -189
- package/lib/remote.js +141 -197
- package/lib/render/branches.js +66 -73
- package/lib/render/chrome.js +264 -323
- package/lib/render/commits.js +187 -500
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -228
- package/lib/render/files.js +45 -99
- package/lib/render/frame.js +129 -183
- package/lib/render/notes.js +144 -160
- package/lib/render/npm.js +234 -112
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +131 -170
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -28
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -38
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -78
- package/lib/report-test.js +256 -141
- package/lib/report-tools.js +96 -161
- package/lib/review-merge.js +114 -152
- package/lib/review.js +115 -189
- package/lib/runs.js +225 -0
- package/lib/select.js +89 -75
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +82 -63
- package/lib/session/branches.js +193 -247
- package/lib/session/change-actions.js +171 -214
- package/lib/session/commit.js +280 -342
- package/lib/session/compose.js +559 -674
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +164 -157
- package/lib/session/edit-click.js +57 -0
- package/lib/session/files-pane.js +85 -53
- package/lib/session/items.js +73 -104
- package/lib/session/lifecycle.js +403 -541
- package/lib/session/load.js +48 -68
- package/lib/session/navigation.js +96 -87
- package/lib/session/npm.js +409 -345
- package/lib/session/ops.js +196 -63
- package/lib/session/pointer.js +23 -15
- package/lib/session/progress.js +33 -21
- package/lib/session/review.js +33 -54
- package/lib/session/tasks.js +333 -0
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -136
- package/lib/session/unit.js +3 -4
- package/lib/session/update.js +76 -97
- package/lib/session/view.js +52 -83
- package/lib/session/watch.js +208 -204
- package/lib/session.js +112 -237
- package/lib/update.js +45 -116
- package/lib/utilities.js +67 -89
- package/lib/wrap.js +33 -44
- package/package.json +1 -1
- package/lib/session/todos.js +0 -293
package/lib/npm-commands.js
CHANGED
|
@@ -9,7 +9,7 @@ const { jsonParse } = metautil;
|
|
|
9
9
|
const ansi = require('./ansi.js');
|
|
10
10
|
const { stripAnsi, visibleWidth } = ansi;
|
|
11
11
|
const utilities = require('./utilities.js');
|
|
12
|
-
const { npmBin, npmOpts, spawnBase } = utilities;
|
|
12
|
+
const { npmBin, npmOpts, spawnBase, unquote, dateStamp } = utilities;
|
|
13
13
|
|
|
14
14
|
const MANIFEST = 'package.json';
|
|
15
15
|
const SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies'];
|
|
@@ -36,7 +36,7 @@ const writeManifest = (root, pkg) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const scriptEntries = (pkg) => {
|
|
39
|
-
const scripts = pkg
|
|
39
|
+
const scripts = pkg.scripts;
|
|
40
40
|
if (!scripts || typeof scripts !== 'object') return [];
|
|
41
41
|
const entries = [];
|
|
42
42
|
for (const name of Object.keys(scripts)) {
|
|
@@ -56,11 +56,8 @@ const binNames = (pkg) => {
|
|
|
56
56
|
return base ? [base] : [];
|
|
57
57
|
}
|
|
58
58
|
if (!bin || typeof bin !== 'object' || Array.isArray(bin)) return [];
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
if (typeof bin[name] === 'string' && bin[name]) names.push(name);
|
|
62
|
-
}
|
|
63
|
-
return names;
|
|
59
|
+
const isPath = (name) => typeof bin[name] === 'string' && bin[name] !== '';
|
|
60
|
+
return Object.keys(bin).filter(isPath);
|
|
64
61
|
};
|
|
65
62
|
|
|
66
63
|
const declaredNames = (pkg) => {
|
|
@@ -68,7 +65,7 @@ const declaredNames = (pkg) => {
|
|
|
68
65
|
for (const section of SECTIONS) {
|
|
69
66
|
const map = pkg[section];
|
|
70
67
|
if (!map || typeof map !== 'object') continue;
|
|
71
|
-
|
|
68
|
+
names.push(...Object.keys(map));
|
|
72
69
|
}
|
|
73
70
|
return names;
|
|
74
71
|
};
|
|
@@ -107,36 +104,50 @@ const isPassingTest = (line) => {
|
|
|
107
104
|
if (!text) return false;
|
|
108
105
|
if (/^\s*[✔✓√] /.test(plain)) return true;
|
|
109
106
|
if (/^ok \d+ /.test(text)) return true;
|
|
110
|
-
|
|
111
|
-
return false;
|
|
107
|
+
return /^\s*PASS\b/.test(plain);
|
|
112
108
|
};
|
|
113
109
|
|
|
114
|
-
const isNodeFrame = (line) => stripAnsi(line).includes('(node:');
|
|
115
|
-
|
|
116
110
|
const indentStack = (line) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const lead = line.match(/^\s*/)?.[0] ?? '';
|
|
120
|
-
const body = lead ? line.slice(lead.length) : line;
|
|
121
|
-
return ` ${body}`;
|
|
111
|
+
if (!/^\s*at\s/.test(stripAnsi(line))) return line;
|
|
112
|
+
return ` ${line.trimStart()}`;
|
|
122
113
|
};
|
|
123
114
|
|
|
124
115
|
const withoutNodeFrame = (line) => {
|
|
125
|
-
if (!isNodeFrame(line)) return line;
|
|
126
116
|
const plain = stripAnsi(line);
|
|
127
|
-
if (
|
|
128
|
-
return null;
|
|
117
|
+
if (!plain.includes('(node:')) return line;
|
|
118
|
+
return /\{\s*$/.test(plain) ? '{' : null;
|
|
129
119
|
};
|
|
130
120
|
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
121
|
+
const withSep = (dir) => {
|
|
122
|
+
const sep = dir.includes('\\') ? '\\' : '/';
|
|
123
|
+
if (dir.endsWith(sep)) return dir;
|
|
124
|
+
return `${dir}${sep}`;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const stripOne = (text, dir) => {
|
|
128
|
+
if (!dir) return text;
|
|
129
|
+
let next = text.split(withSep(dir)).join('');
|
|
130
|
+
if (next.includes(dir)) next = next.split(dir).join('.');
|
|
131
|
+
return next;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const relativize = (text, root) => {
|
|
135
|
+
if (!root) return text;
|
|
136
|
+
const slash = root.replaceAll('\\', '/');
|
|
137
|
+
const back = slash.replaceAll('/', '\\');
|
|
138
|
+
const resolved = path.resolve(root).replaceAll('\\', '/');
|
|
139
|
+
const resolvedBack = resolved.replaceAll('/', '\\');
|
|
140
|
+
let next = text;
|
|
141
|
+
for (const dir of [slash, back, resolved, resolvedBack]) {
|
|
142
|
+
next = stripOne(next, dir);
|
|
143
|
+
}
|
|
144
|
+
return next;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const traceLine = (line, root) => {
|
|
148
|
+
const next = withoutNodeFrame(line);
|
|
149
|
+
if (next === null) return null;
|
|
150
|
+
return relativize(indentStack(next), root);
|
|
140
151
|
};
|
|
141
152
|
|
|
142
153
|
const objectEnd = (lines, start) => {
|
|
@@ -173,19 +184,18 @@ const showValue = (value) => {
|
|
|
173
184
|
};
|
|
174
185
|
|
|
175
186
|
const parseObject = (lines) => {
|
|
176
|
-
const body = lines.slice(1, -1);
|
|
177
187
|
try {
|
|
178
188
|
const value = JSON.parse(lines.join('\n'));
|
|
179
189
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
180
190
|
return null;
|
|
181
191
|
}
|
|
182
|
-
const entries = [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
const entries = Object.keys(value).map((key) => [
|
|
193
|
+
key,
|
|
194
|
+
showValue(value[key]),
|
|
195
|
+
]);
|
|
186
196
|
return entries.length ? entries : null;
|
|
187
197
|
} catch {
|
|
188
|
-
return parseInspect(
|
|
198
|
+
return parseInspect(lines.slice(1, -1));
|
|
189
199
|
}
|
|
190
200
|
};
|
|
191
201
|
|
|
@@ -193,18 +203,22 @@ const TABLE_KEY = '\x1b[900m';
|
|
|
193
203
|
const TABLE_VAL = '\x1b[901m';
|
|
194
204
|
|
|
195
205
|
const formatTable = (entries) => {
|
|
196
|
-
|
|
206
|
+
const prepared = [];
|
|
207
|
+
let keyWidth = 0;
|
|
197
208
|
for (const entry of entries) {
|
|
198
|
-
|
|
209
|
+
const lines = `${entry[1]}`.split('\n').filter((line) => line.trim());
|
|
210
|
+
if (!lines.length) continue;
|
|
211
|
+
prepared.push([entry[0], lines]);
|
|
212
|
+
keyWidth = Math.max(keyWidth, visibleWidth(entry[0]));
|
|
199
213
|
}
|
|
200
214
|
const rows = [];
|
|
201
|
-
for (const entry of
|
|
202
|
-
const
|
|
203
|
-
for (let i = 0; i <
|
|
215
|
+
for (const entry of prepared) {
|
|
216
|
+
const lines = entry[1];
|
|
217
|
+
for (let i = 0; i < lines.length; i++) {
|
|
204
218
|
const name = i === 0 ? entry[0] : '';
|
|
205
|
-
const pad = ' '.repeat(Math.max(0,
|
|
219
|
+
const pad = ' '.repeat(Math.max(0, keyWidth - visibleWidth(name)));
|
|
206
220
|
const key = ` ${pad}${name} `;
|
|
207
|
-
const value = ` ${
|
|
221
|
+
const value = ` ${lines[i]} `;
|
|
208
222
|
rows.push(`${TABLE_KEY}${key}${TABLE_VAL} ${value}`);
|
|
209
223
|
}
|
|
210
224
|
}
|
|
@@ -215,67 +229,30 @@ const foldObjects = (lines) => {
|
|
|
215
229
|
const out = [];
|
|
216
230
|
for (let i = 0; i < lines.length; i++) {
|
|
217
231
|
const end = objectEnd(lines, i);
|
|
218
|
-
|
|
219
|
-
out.push(lines[i]);
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
const entries = parseObject(lines.slice(i, end + 1));
|
|
232
|
+
const entries = end < 0 ? null : parseObject(lines.slice(i, end + 1));
|
|
223
233
|
if (!entries) {
|
|
224
234
|
out.push(lines[i]);
|
|
225
235
|
continue;
|
|
226
236
|
}
|
|
227
237
|
if (out.length && out[out.length - 1] !== '') out.push('');
|
|
228
|
-
|
|
238
|
+
out.push(...formatTable(entries));
|
|
229
239
|
i = end;
|
|
230
240
|
}
|
|
231
241
|
return out;
|
|
232
242
|
};
|
|
233
243
|
|
|
234
|
-
const withSep = (dir) => {
|
|
235
|
-
const sep = dir.includes('\\') ? '\\' : '/';
|
|
236
|
-
if (dir.endsWith(sep)) return dir;
|
|
237
|
-
return `${dir}${sep}`;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const stripOne = (text, dir) => {
|
|
241
|
-
if (!dir) return text;
|
|
242
|
-
let next = text.split(withSep(dir)).join('');
|
|
243
|
-
if (next.includes(dir)) next = next.split(dir).join('.');
|
|
244
|
-
return next;
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const relativize = (text, root) => {
|
|
248
|
-
if (!root) return text;
|
|
249
|
-
const slash = root.replaceAll('\\', '/');
|
|
250
|
-
const back = slash.replaceAll('/', '\\');
|
|
251
|
-
const resolved = path.resolve(root).replaceAll('\\', '/');
|
|
252
|
-
const resolvedBack = resolved.replaceAll('/', '\\');
|
|
253
|
-
let next = text;
|
|
254
|
-
for (const dir of [slash, back, resolved, resolvedBack]) {
|
|
255
|
-
next = stripOne(next, dir);
|
|
256
|
-
}
|
|
257
|
-
return next;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
244
|
const reduceOutput = (text, root, status) => {
|
|
261
|
-
const lines = `${text ?? ''}`.split(/\r?\n/);
|
|
262
245
|
const kept = [];
|
|
263
|
-
for (const line of
|
|
246
|
+
for (const line of `${text ?? ''}`.split(/\r?\n/)) {
|
|
264
247
|
if (isPassingTest(line)) continue;
|
|
265
|
-
const next =
|
|
266
|
-
if (next
|
|
267
|
-
kept.push(relativize(indentStack(next), root));
|
|
248
|
+
const next = traceLine(line, root);
|
|
249
|
+
if (next !== null) kept.push(next);
|
|
268
250
|
}
|
|
269
251
|
const folded = foldObjects(kept);
|
|
270
252
|
while (folded.length && folded[folded.length - 1] === '') folded.pop();
|
|
271
|
-
if (status
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
const code = status === undefined || status === null ? 'null' : status;
|
|
276
|
-
const exit = `exit ${code}`;
|
|
277
|
-
if (!folded.length) return `${exit}\n`;
|
|
278
|
-
return `${folded.join('\n')}\n${exit}\n`;
|
|
253
|
+
if (status !== '') folded.push(`exit ${status ?? 'null'}`);
|
|
254
|
+
if (!folded.length) return '';
|
|
255
|
+
return `${folded.join('\n')}\n`;
|
|
279
256
|
};
|
|
280
257
|
|
|
281
258
|
const logFileName = (command, date, index) => {
|
|
@@ -284,13 +261,6 @@ const logFileName = (command, date, index) => {
|
|
|
284
261
|
return `${date}-${safe}-${n}.log`;
|
|
285
262
|
};
|
|
286
263
|
|
|
287
|
-
const dateStamp = (now) => {
|
|
288
|
-
const y = now.getFullYear();
|
|
289
|
-
const m = `${now.getMonth() + 1}`.padStart(2, '0');
|
|
290
|
-
const d = `${now.getDate()}`.padStart(2, '0');
|
|
291
|
-
return `${y}-${m}-${d}`;
|
|
292
|
-
};
|
|
293
|
-
|
|
294
264
|
const LOG_DIR = '.log';
|
|
295
265
|
const LOG_STALE_DAYS = 5;
|
|
296
266
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -326,10 +296,9 @@ const readLogNames = (dir) => {
|
|
|
326
296
|
|
|
327
297
|
const staleLogFiles = (root, now = new Date()) => {
|
|
328
298
|
const dir = path.join(root, LOG_DIR);
|
|
329
|
-
const names = readLogNames(dir);
|
|
330
299
|
const files = [];
|
|
331
300
|
let bytes = 0;
|
|
332
|
-
for (const name of
|
|
301
|
+
for (const name of readLogNames(dir)) {
|
|
333
302
|
if (!name.endsWith('.log') || !isStaleLog(name, now)) continue;
|
|
334
303
|
const full = path.join(dir, name);
|
|
335
304
|
let stat;
|
|
@@ -352,31 +321,22 @@ const removeStaleLogs = (root, now = new Date()) => {
|
|
|
352
321
|
return found.bytes;
|
|
353
322
|
};
|
|
354
323
|
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const formatSize = (bytes) => {
|
|
361
|
-
const size = bytes > 0 ? bytes : 0;
|
|
362
|
-
if (size < 1024) return `${size}b`;
|
|
363
|
-
const kb = size / 1024;
|
|
364
|
-
if (kb < 1024) return `${scaled(kb)}k`;
|
|
365
|
-
return `${scaled(kb / 1024)}m`;
|
|
324
|
+
const logSlot = (dir, name) => {
|
|
325
|
+
const rawName = name.replace(/\.log$/, '.raw.log');
|
|
326
|
+
const full = path.join(dir, name);
|
|
327
|
+
const rawFull = path.join(dir, rawName);
|
|
328
|
+
return { name, full, dir, rawName, rawFull };
|
|
366
329
|
};
|
|
367
330
|
|
|
368
331
|
const nextLogFile = (root, command, now = new Date()) => {
|
|
369
332
|
const date = dateStamp(now);
|
|
370
333
|
const dir = path.join(root, LOG_DIR);
|
|
371
|
-
let
|
|
372
|
-
let
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
index += 1;
|
|
376
|
-
name = logFileName(command, date, index);
|
|
377
|
-
full = path.join(dir, name);
|
|
334
|
+
let slot = null;
|
|
335
|
+
for (let index = 1; index <= 100; index++) {
|
|
336
|
+
slot = logSlot(dir, logFileName(command, date, index));
|
|
337
|
+
if (!fs.existsSync(slot.full) && !fs.existsSync(slot.rawFull)) break;
|
|
378
338
|
}
|
|
379
|
-
return
|
|
339
|
+
return slot;
|
|
380
340
|
};
|
|
381
341
|
|
|
382
342
|
const logText = (text) =>
|
|
@@ -385,26 +345,14 @@ const logText = (text) =>
|
|
|
385
345
|
.map((line) => line.trimEnd())
|
|
386
346
|
.join('\n');
|
|
387
347
|
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const parseScriptLine = (text) => {
|
|
396
|
-
const raw = `${text ?? ''}`.trim();
|
|
397
|
-
const at = raw.indexOf(': ');
|
|
398
|
-
if (at <= 0) return null;
|
|
399
|
-
const name = raw.slice(0, at).trim();
|
|
400
|
-
const command = raw.slice(at + 2).trim();
|
|
401
|
-
if (!name || !command) return null;
|
|
402
|
-
if (!/^[\w:.-]+$/.test(name)) return null;
|
|
403
|
-
return { name, command };
|
|
348
|
+
const saveLogs = async (slot, reduced, raw) => {
|
|
349
|
+
await fs.promises.mkdir(slot.dir, { recursive: true });
|
|
350
|
+
await Promise.all([
|
|
351
|
+
fs.promises.writeFile(slot.full, logText(reduced)),
|
|
352
|
+
fs.promises.writeFile(slot.rawFull, logText(raw)),
|
|
353
|
+
]);
|
|
404
354
|
};
|
|
405
355
|
|
|
406
|
-
const scriptLine = (entry) => `${entry.name}: ${entry.command}`;
|
|
407
|
-
|
|
408
356
|
const orderedScripts = (pkg) => {
|
|
409
357
|
const scripts = pkg.scripts;
|
|
410
358
|
if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts)) {
|
|
@@ -413,47 +361,25 @@ const orderedScripts = (pkg) => {
|
|
|
413
361
|
return scripts;
|
|
414
362
|
};
|
|
415
363
|
|
|
416
|
-
const
|
|
417
|
-
const scripts = orderedScripts(pkg);
|
|
418
|
-
const names = Object.keys(scripts);
|
|
364
|
+
const pickScripts = (scripts, names) => {
|
|
419
365
|
const ordered = {};
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (key === prev) ordered[next.name] = next.command;
|
|
423
|
-
else if (key !== next.name) ordered[key] = scripts[key];
|
|
424
|
-
}
|
|
425
|
-
} else {
|
|
426
|
-
for (const key of names) ordered[key] = scripts[key];
|
|
427
|
-
ordered[next.name] = next.command;
|
|
428
|
-
}
|
|
429
|
-
pkg.scripts = ordered;
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
const dropScript = (pkg, name) => {
|
|
433
|
-
const scripts = orderedScripts(pkg);
|
|
434
|
-
if (!Object.hasOwn(scripts, name)) return false;
|
|
435
|
-
const ordered = {};
|
|
436
|
-
for (const key of Object.keys(scripts)) {
|
|
437
|
-
if (key !== name) ordered[key] = scripts[key];
|
|
438
|
-
}
|
|
439
|
-
pkg.scripts = ordered;
|
|
440
|
-
return true;
|
|
366
|
+
for (const key of names) ordered[key] = scripts[key];
|
|
367
|
+
return ordered;
|
|
441
368
|
};
|
|
442
369
|
|
|
443
|
-
const
|
|
370
|
+
const upsertScript = (pkg, prev, next) => {
|
|
444
371
|
const scripts = orderedScripts(pkg);
|
|
445
372
|
const names = Object.keys(scripts);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const swap = names[at];
|
|
451
|
-
names[at] = names[next];
|
|
452
|
-
names[next] = swap;
|
|
373
|
+
if (!prev || !names.includes(prev)) {
|
|
374
|
+
pkg.scripts = { ...scripts, [next.name]: next.command };
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
453
377
|
const ordered = {};
|
|
454
|
-
for (const key of names)
|
|
378
|
+
for (const key of names) {
|
|
379
|
+
if (key === prev) ordered[next.name] = next.command;
|
|
380
|
+
else if (key !== next.name) ordered[key] = scripts[key];
|
|
381
|
+
}
|
|
455
382
|
pkg.scripts = ordered;
|
|
456
|
-
return true;
|
|
457
383
|
};
|
|
458
384
|
|
|
459
385
|
const saveScript = (root, prev, next) => {
|
|
@@ -464,23 +390,30 @@ const saveScript = (root, prev, next) => {
|
|
|
464
390
|
|
|
465
391
|
const removeScript = (root, name) => {
|
|
466
392
|
const pkg = readManifest(root);
|
|
467
|
-
if (!pkg
|
|
393
|
+
if (!pkg) return false;
|
|
394
|
+
const scripts = orderedScripts(pkg);
|
|
395
|
+
if (!Object.hasOwn(scripts, name)) return false;
|
|
396
|
+
const names = Object.keys(scripts).filter((key) => key !== name);
|
|
397
|
+
pkg.scripts = pickScripts(scripts, names);
|
|
468
398
|
writeManifest(root, pkg);
|
|
469
399
|
return true;
|
|
470
400
|
};
|
|
471
401
|
|
|
472
402
|
const reorderScript = (root, name, delta) => {
|
|
473
403
|
const pkg = readManifest(root);
|
|
474
|
-
if (!pkg
|
|
404
|
+
if (!pkg) return false;
|
|
405
|
+
const scripts = orderedScripts(pkg);
|
|
406
|
+
const names = Object.keys(scripts);
|
|
407
|
+
const at = names.indexOf(name);
|
|
408
|
+
const next = at + delta;
|
|
409
|
+
if (at < 0 || next < 0 || next >= names.length) return false;
|
|
410
|
+
names[at] = names[next];
|
|
411
|
+
names[next] = name;
|
|
412
|
+
pkg.scripts = pickScripts(scripts, names);
|
|
475
413
|
writeManifest(root, pkg);
|
|
476
414
|
return true;
|
|
477
415
|
};
|
|
478
416
|
|
|
479
|
-
const npmArgs = (entry) => {
|
|
480
|
-
if (entry.kind === 'bin') return ['exec', '--', entry.name];
|
|
481
|
-
return ['run', entry.name];
|
|
482
|
-
};
|
|
483
|
-
|
|
484
417
|
const signalPid = (pid, signal) => {
|
|
485
418
|
try {
|
|
486
419
|
process.kill(pid, signal);
|
|
@@ -491,7 +424,7 @@ const signalPid = (pid, signal) => {
|
|
|
491
424
|
};
|
|
492
425
|
|
|
493
426
|
const killTree = (proc) => {
|
|
494
|
-
const pid = proc
|
|
427
|
+
const pid = proc.pid;
|
|
495
428
|
if (!pid) return;
|
|
496
429
|
if (process.platform === 'win32') {
|
|
497
430
|
const killer = spawn(
|
|
@@ -528,6 +461,8 @@ const startNpm = (cwd, entry, onData, onClose) => {
|
|
|
528
461
|
settled = true;
|
|
529
462
|
onClose({ status, text: raw });
|
|
530
463
|
};
|
|
464
|
+
const isBin = entry.kind === 'bin';
|
|
465
|
+
const args = isBin ? ['exec', '--', entry.name] : ['run', entry.name];
|
|
531
466
|
let child;
|
|
532
467
|
try {
|
|
533
468
|
const opts = npmOpts({
|
|
@@ -535,7 +470,7 @@ const startNpm = (cwd, entry, onData, onClose) => {
|
|
|
535
470
|
env: commandEnv(),
|
|
536
471
|
detached: process.platform !== 'win32',
|
|
537
472
|
});
|
|
538
|
-
child = spawn(npmBin(),
|
|
473
|
+
child = spawn(npmBin(), args, opts);
|
|
539
474
|
} catch (error) {
|
|
540
475
|
raw = `${error.message}\n`;
|
|
541
476
|
finish(1);
|
|
@@ -546,13 +481,10 @@ const startNpm = (cwd, entry, onData, onClose) => {
|
|
|
546
481
|
raw += chunk;
|
|
547
482
|
onData(raw);
|
|
548
483
|
};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (child.stderr) {
|
|
554
|
-
child.stderr.setEncoding('utf8');
|
|
555
|
-
child.stderr.on('data', push);
|
|
484
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
485
|
+
if (!stream) continue;
|
|
486
|
+
stream.setEncoding('utf8');
|
|
487
|
+
stream.on('data', push);
|
|
556
488
|
}
|
|
557
489
|
child.on('error', (error) => {
|
|
558
490
|
if (!stopped) raw += `${error.message}\n`;
|
|
@@ -572,21 +504,19 @@ module.exports = {
|
|
|
572
504
|
listCommands,
|
|
573
505
|
reduceOutput,
|
|
574
506
|
logFileName,
|
|
575
|
-
|
|
507
|
+
nextLogFile,
|
|
508
|
+
saveLogs,
|
|
576
509
|
staleLogFiles,
|
|
577
510
|
removeStaleLogs,
|
|
578
|
-
formatSize,
|
|
579
|
-
parseScriptLine,
|
|
580
|
-
scriptLine,
|
|
581
511
|
saveScript,
|
|
582
512
|
removeScript,
|
|
583
513
|
reorderScript,
|
|
584
514
|
startNpm,
|
|
585
515
|
commandEnv,
|
|
586
516
|
isPassingTest,
|
|
587
|
-
|
|
588
|
-
indentStack,
|
|
517
|
+
traceLine,
|
|
589
518
|
relativize,
|
|
590
519
|
TABLE_KEY,
|
|
591
520
|
TABLE_VAL,
|
|
521
|
+
formatTable,
|
|
592
522
|
};
|