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.
Files changed (114) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/LICENSE +1 -1
  3. package/README.md +11 -11
  4. package/bin/reslop.js +3 -8
  5. package/lib/ansi.js +72 -19
  6. package/lib/api.js +1 -1
  7. package/lib/capabilities.js +8 -8
  8. package/lib/cli.js +61 -75
  9. package/lib/clipboard.js +39 -47
  10. package/lib/dashboard/git.js +128 -0
  11. package/lib/dashboard/model.js +357 -0
  12. package/lib/dashboard/npm.js +290 -0
  13. package/lib/dashboard/tree.js +278 -0
  14. package/lib/deps-diff.js +197 -402
  15. package/lib/deps.js +113 -240
  16. package/lib/detect.js +8 -18
  17. package/lib/diff/align.js +41 -113
  18. package/lib/diff/diff.js +12 -43
  19. package/lib/diff/display.js +25 -36
  20. package/lib/diff/edit.js +6 -20
  21. package/lib/diff/inline.js +76 -144
  22. package/lib/diff/items.js +33 -47
  23. package/lib/diff/parse.js +28 -56
  24. package/lib/diff/patch.js +12 -33
  25. package/lib/diff/unit.js +79 -178
  26. package/lib/editor.js +52 -69
  27. package/lib/files.js +79 -91
  28. package/lib/find.js +61 -0
  29. package/lib/git-branches.js +254 -416
  30. package/lib/git-deps.js +148 -272
  31. package/lib/git-reword-todo.js +5 -13
  32. package/lib/git-worktree.js +161 -214
  33. package/lib/git.js +21 -49
  34. package/lib/github.js +67 -181
  35. package/lib/gitlab.js +40 -90
  36. package/lib/highlight/bash.js +55 -177
  37. package/lib/highlight/core.js +155 -141
  38. package/lib/highlight/css.js +25 -58
  39. package/lib/highlight/csv.js +19 -37
  40. package/lib/highlight/dart.js +72 -213
  41. package/lib/highlight/dot.js +14 -14
  42. package/lib/highlight/html.js +42 -63
  43. package/lib/highlight/js.js +177 -506
  44. package/lib/highlight/json.js +17 -37
  45. package/lib/highlight/log.js +19 -25
  46. package/lib/highlight/txt.js +7 -10
  47. package/lib/highlight.js +17 -46
  48. package/lib/keys.js +75 -83
  49. package/lib/manifest.js +67 -126
  50. package/lib/npm-commands.js +120 -169
  51. package/lib/remote.js +140 -190
  52. package/lib/render/branches.js +63 -61
  53. package/lib/render/chrome.js +412 -347
  54. package/lib/render/commits.js +160 -361
  55. package/lib/render/dash-activity.js +212 -0
  56. package/lib/render/dash-blocks.js +496 -0
  57. package/lib/render/dash-table.js +295 -0
  58. package/lib/render/dashboard.js +62 -0
  59. package/lib/render/diff.js +123 -258
  60. package/lib/render/files.js +60 -86
  61. package/lib/render/frame.js +82 -117
  62. package/lib/render/notes.js +150 -210
  63. package/lib/render/npm.js +213 -177
  64. package/lib/render/packages.js +93 -0
  65. package/lib/render/present.js +4 -11
  66. package/lib/render/primitives.js +77 -172
  67. package/lib/render/render.js +5 -14
  68. package/lib/render/report.js +43 -0
  69. package/lib/render/repos.js +226 -0
  70. package/lib/render/search.js +25 -0
  71. package/lib/render/tiles.js +355 -0
  72. package/lib/render/unit.js +15 -31
  73. package/lib/report-model.js +110 -33
  74. package/lib/report-parse.js +56 -37
  75. package/lib/report-render.js +102 -59
  76. package/lib/report-run.js +87 -77
  77. package/lib/report-test.js +221 -99
  78. package/lib/report-tools.js +96 -154
  79. package/lib/review-merge.js +33 -41
  80. package/lib/review.js +69 -129
  81. package/lib/runs.js +225 -0
  82. package/lib/select.js +53 -91
  83. package/lib/session/accessors.js +14 -21
  84. package/lib/session/actions.js +189 -65
  85. package/lib/session/branches.js +164 -222
  86. package/lib/session/change-actions.js +49 -119
  87. package/lib/session/commit.js +298 -352
  88. package/lib/session/compose.js +572 -715
  89. package/lib/session/dashboard.js +530 -0
  90. package/lib/session/dispatch.js +197 -171
  91. package/lib/session/files-pane.js +92 -45
  92. package/lib/session/find.js +94 -0
  93. package/lib/session/items.js +6 -17
  94. package/lib/session/lifecycle.js +420 -466
  95. package/lib/session/load.js +1 -14
  96. package/lib/session/navigation.js +4 -2
  97. package/lib/session/npm.js +403 -420
  98. package/lib/session/ops.js +5 -13
  99. package/lib/session/packages.js +143 -0
  100. package/lib/session/pointer.js +30 -14
  101. package/lib/session/progress.js +39 -30
  102. package/lib/session/review.js +1 -3
  103. package/lib/session/tasks.js +277 -236
  104. package/lib/session/templates.js +51 -59
  105. package/lib/session/terminal.js +85 -132
  106. package/lib/session/view.js +59 -80
  107. package/lib/session/watch.js +201 -212
  108. package/lib/session/workspace.js +455 -0
  109. package/lib/session.js +95 -161
  110. package/lib/update.js +44 -111
  111. package/lib/utilities.js +46 -88
  112. package/lib/workspace.js +166 -0
  113. package/lib/wrap.js +29 -55
  114. package/package.json +1 -1
@@ -36,7 +36,7 @@ const writeManifest = (root, pkg) => {
36
36
  };
37
37
 
38
38
  const scriptEntries = (pkg) => {
39
- const scripts = pkg && pkg.scripts;
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 names = [];
60
- for (const name of Object.keys(bin)) {
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
- for (const name of Object.keys(map)) names.push(name);
68
+ names.push(...Object.keys(map));
72
69
  }
73
70
  return names;
74
71
  };
@@ -107,25 +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
- if (/^\s*PASS\b/.test(plain)) return true;
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
- const plain = stripAnsi(line);
118
- if (!/^\s*at\s/.test(plain)) return line;
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 (/\{\s*$/.test(plain)) return '{';
128
- return null;
117
+ if (!plain.includes('(node:')) return line;
118
+ return /\{\s*$/.test(plain) ? '{' : null;
119
+ };
120
+
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);
129
151
  };
130
152
 
131
153
  const objectEnd = (lines, start) => {
@@ -162,19 +184,18 @@ const showValue = (value) => {
162
184
  };
163
185
 
164
186
  const parseObject = (lines) => {
165
- const body = lines.slice(1, -1);
166
187
  try {
167
188
  const value = JSON.parse(lines.join('\n'));
168
189
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
169
190
  return null;
170
191
  }
171
- const entries = [];
172
- for (const key of Object.keys(value)) {
173
- entries.push([key, showValue(value[key])]);
174
- }
192
+ const entries = Object.keys(value).map((key) => [
193
+ key,
194
+ showValue(value[key]),
195
+ ]);
175
196
  return entries.length ? entries : null;
176
197
  } catch {
177
- return parseInspect(body);
198
+ return parseInspect(lines.slice(1, -1));
178
199
  }
179
200
  };
180
201
 
@@ -182,18 +203,22 @@ const TABLE_KEY = '\x1b[900m';
182
203
  const TABLE_VAL = '\x1b[901m';
183
204
 
184
205
  const formatTable = (entries) => {
185
- let keyW = 0;
206
+ const prepared = [];
207
+ let keyWidth = 0;
186
208
  for (const entry of entries) {
187
- keyW = Math.max(keyW, visibleWidth(entry[0]));
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]));
188
213
  }
189
214
  const rows = [];
190
- for (const entry of entries) {
191
- const parts = `${entry[1]}`.split('\n');
192
- for (let i = 0; i < parts.length; i++) {
215
+ for (const entry of prepared) {
216
+ const lines = entry[1];
217
+ for (let i = 0; i < lines.length; i++) {
193
218
  const name = i === 0 ? entry[0] : '';
194
- const pad = ' '.repeat(Math.max(0, keyW - visibleWidth(name)));
219
+ const pad = ' '.repeat(Math.max(0, keyWidth - visibleWidth(name)));
195
220
  const key = ` ${pad}${name} `;
196
- const value = ` ${parts[i]} `;
221
+ const value = ` ${lines[i]} `;
197
222
  rows.push(`${TABLE_KEY}${key}${TABLE_VAL} ${value}`);
198
223
  }
199
224
  }
@@ -204,67 +229,30 @@ const foldObjects = (lines) => {
204
229
  const out = [];
205
230
  for (let i = 0; i < lines.length; i++) {
206
231
  const end = objectEnd(lines, i);
207
- if (end < 0) {
208
- out.push(lines[i]);
209
- continue;
210
- }
211
- const entries = parseObject(lines.slice(i, end + 1));
232
+ const entries = end < 0 ? null : parseObject(lines.slice(i, end + 1));
212
233
  if (!entries) {
213
234
  out.push(lines[i]);
214
235
  continue;
215
236
  }
216
237
  if (out.length && out[out.length - 1] !== '') out.push('');
217
- for (const row of formatTable(entries)) out.push(row);
238
+ out.push(...formatTable(entries));
218
239
  i = end;
219
240
  }
220
241
  return out;
221
242
  };
222
243
 
223
- const withSep = (dir) => {
224
- const sep = dir.includes('\\') ? '\\' : '/';
225
- if (dir.endsWith(sep)) return dir;
226
- return `${dir}${sep}`;
227
- };
228
-
229
- const stripOne = (text, dir) => {
230
- if (!dir) return text;
231
- let next = text.split(withSep(dir)).join('');
232
- if (next.includes(dir)) next = next.split(dir).join('.');
233
- return next;
234
- };
235
-
236
- const relativize = (text, root) => {
237
- if (!root) return text;
238
- const slash = root.replaceAll('\\', '/');
239
- const back = slash.replaceAll('/', '\\');
240
- const resolved = path.resolve(root).replaceAll('\\', '/');
241
- const resolvedBack = resolved.replaceAll('/', '\\');
242
- let next = text;
243
- for (const dir of [slash, back, resolved, resolvedBack]) {
244
- next = stripOne(next, dir);
245
- }
246
- return next;
247
- };
248
-
249
244
  const reduceOutput = (text, root, status) => {
250
- const lines = `${text ?? ''}`.split(/\r?\n/);
251
245
  const kept = [];
252
- for (const line of lines) {
246
+ for (const line of `${text ?? ''}`.split(/\r?\n/)) {
253
247
  if (isPassingTest(line)) continue;
254
- const next = withoutNodeFrame(line);
255
- if (next === null) continue;
256
- kept.push(relativize(indentStack(next), root));
248
+ const next = traceLine(line, root);
249
+ if (next !== null) kept.push(next);
257
250
  }
258
251
  const folded = foldObjects(kept);
259
252
  while (folded.length && folded[folded.length - 1] === '') folded.pop();
260
- if (status === '') {
261
- if (!folded.length) return '';
262
- return `${folded.join('\n')}\n`;
263
- }
264
- const code = status === undefined || status === null ? 'null' : status;
265
- const exit = `exit ${code}`;
266
- if (!folded.length) return `${exit}\n`;
267
- 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`;
268
256
  };
269
257
 
270
258
  const logFileName = (command, date, index) => {
@@ -308,10 +296,9 @@ const readLogNames = (dir) => {
308
296
 
309
297
  const staleLogFiles = (root, now = new Date()) => {
310
298
  const dir = path.join(root, LOG_DIR);
311
- const names = readLogNames(dir);
312
299
  const files = [];
313
300
  let bytes = 0;
314
- for (const name of names) {
301
+ for (const name of readLogNames(dir)) {
315
302
  if (!name.endsWith('.log') || !isStaleLog(name, now)) continue;
316
303
  const full = path.join(dir, name);
317
304
  let stat;
@@ -334,31 +321,22 @@ const removeStaleLogs = (root, now = new Date()) => {
334
321
  return found.bytes;
335
322
  };
336
323
 
337
- const scaled = (value) => {
338
- if (value >= 10) return `${Math.round(value)}`;
339
- return `${Math.round(value * 10) / 10}`;
340
- };
341
-
342
- const formatSize = (bytes) => {
343
- const size = bytes > 0 ? bytes : 0;
344
- if (size < 1024) return `${size}b`;
345
- const kb = size / 1024;
346
- if (kb < 1024) return `${scaled(kb)}k`;
347
- 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 };
348
329
  };
349
330
 
350
331
  const nextLogFile = (root, command, now = new Date()) => {
351
332
  const date = dateStamp(now);
352
333
  const dir = path.join(root, LOG_DIR);
353
- let index = 1;
354
- let name = logFileName(command, date, index);
355
- let full = path.join(dir, name);
356
- while (index < 100 && fs.existsSync(full)) {
357
- index += 1;
358
- name = logFileName(command, date, index);
359
- 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;
360
338
  }
361
- return { name, full, dir };
339
+ return slot;
362
340
  };
363
341
 
364
342
  const logText = (text) =>
@@ -367,22 +345,12 @@ const logText = (text) =>
367
345
  .map((line) => line.trimEnd())
368
346
  .join('\n');
369
347
 
370
- const writeLog = (root, command, text, now) => {
371
- const file = nextLogFile(root, command, now);
372
- fs.mkdirSync(file.dir, { recursive: true });
373
- fs.writeFileSync(file.full, logText(text));
374
- return file.name;
375
- };
376
-
377
- const parseScriptLine = (text) => {
378
- const raw = `${text ?? ''}`.trim();
379
- const at = raw.indexOf(': ');
380
- if (at <= 0) return null;
381
- const name = raw.slice(0, at).trim();
382
- const command = raw.slice(at + 2).trim();
383
- if (!name || !command) return null;
384
- if (!/^[\w:.-]+$/.test(name)) return null;
385
- 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
+ ]);
386
354
  };
387
355
 
388
356
  const orderedScripts = (pkg) => {
@@ -393,47 +361,25 @@ const orderedScripts = (pkg) => {
393
361
  return scripts;
394
362
  };
395
363
 
396
- const upsertScript = (pkg, prev, next) => {
397
- const scripts = orderedScripts(pkg);
398
- const names = Object.keys(scripts);
399
- const ordered = {};
400
- if (prev && names.includes(prev)) {
401
- for (const key of names) {
402
- if (key === prev) ordered[next.name] = next.command;
403
- else if (key !== next.name) ordered[key] = scripts[key];
404
- }
405
- } else {
406
- for (const key of names) ordered[key] = scripts[key];
407
- ordered[next.name] = next.command;
408
- }
409
- pkg.scripts = ordered;
410
- };
411
-
412
- const dropScript = (pkg, name) => {
413
- const scripts = orderedScripts(pkg);
414
- if (!Object.hasOwn(scripts, name)) return false;
364
+ const pickScripts = (scripts, names) => {
415
365
  const ordered = {};
416
- for (const key of Object.keys(scripts)) {
417
- if (key !== name) ordered[key] = scripts[key];
418
- }
419
- pkg.scripts = ordered;
420
- return true;
366
+ for (const key of names) ordered[key] = scripts[key];
367
+ return ordered;
421
368
  };
422
369
 
423
- const moveScript = (pkg, name, delta) => {
370
+ const upsertScript = (pkg, prev, next) => {
424
371
  const scripts = orderedScripts(pkg);
425
372
  const names = Object.keys(scripts);
426
- const at = names.indexOf(name);
427
- if (at < 0) return false;
428
- const next = at + delta;
429
- if (next < 0 || next >= names.length) return false;
430
- const swap = names[at];
431
- names[at] = names[next];
432
- names[next] = swap;
373
+ if (!prev || !names.includes(prev)) {
374
+ pkg.scripts = { ...scripts, [next.name]: next.command };
375
+ return;
376
+ }
433
377
  const ordered = {};
434
- for (const key of names) ordered[key] = scripts[key];
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
+ }
435
382
  pkg.scripts = ordered;
436
- return true;
437
383
  };
438
384
 
439
385
  const saveScript = (root, prev, next) => {
@@ -444,23 +390,30 @@ const saveScript = (root, prev, next) => {
444
390
 
445
391
  const removeScript = (root, name) => {
446
392
  const pkg = readManifest(root);
447
- if (!pkg || !dropScript(pkg, name)) return false;
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);
448
398
  writeManifest(root, pkg);
449
399
  return true;
450
400
  };
451
401
 
452
402
  const reorderScript = (root, name, delta) => {
453
403
  const pkg = readManifest(root);
454
- if (!pkg || !moveScript(pkg, name, delta)) return false;
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);
455
413
  writeManifest(root, pkg);
456
414
  return true;
457
415
  };
458
416
 
459
- const npmArgs = (entry) => {
460
- if (entry.kind === 'bin') return ['exec', '--', entry.name];
461
- return ['run', entry.name];
462
- };
463
-
464
417
  const signalPid = (pid, signal) => {
465
418
  try {
466
419
  process.kill(pid, signal);
@@ -471,7 +424,7 @@ const signalPid = (pid, signal) => {
471
424
  };
472
425
 
473
426
  const killTree = (proc) => {
474
- const pid = proc && proc.pid;
427
+ const pid = proc.pid;
475
428
  if (!pid) return;
476
429
  if (process.platform === 'win32') {
477
430
  const killer = spawn(
@@ -508,6 +461,8 @@ const startNpm = (cwd, entry, onData, onClose) => {
508
461
  settled = true;
509
462
  onClose({ status, text: raw });
510
463
  };
464
+ const isBin = entry.kind === 'bin';
465
+ const args = isBin ? ['exec', '--', entry.name] : ['run', entry.name];
511
466
  let child;
512
467
  try {
513
468
  const opts = npmOpts({
@@ -515,7 +470,7 @@ const startNpm = (cwd, entry, onData, onClose) => {
515
470
  env: commandEnv(),
516
471
  detached: process.platform !== 'win32',
517
472
  });
518
- child = spawn(npmBin(), npmArgs(entry), opts);
473
+ child = spawn(npmBin(), args, opts);
519
474
  } catch (error) {
520
475
  raw = `${error.message}\n`;
521
476
  finish(1);
@@ -526,13 +481,10 @@ const startNpm = (cwd, entry, onData, onClose) => {
526
481
  raw += chunk;
527
482
  onData(raw);
528
483
  };
529
- if (child.stdout) {
530
- child.stdout.setEncoding('utf8');
531
- child.stdout.on('data', push);
532
- }
533
- if (child.stderr) {
534
- child.stderr.setEncoding('utf8');
535
- 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);
536
488
  }
537
489
  child.on('error', (error) => {
538
490
  if (!stopped) raw += `${error.message}\n`;
@@ -552,20 +504,19 @@ module.exports = {
552
504
  listCommands,
553
505
  reduceOutput,
554
506
  logFileName,
555
- writeLog,
507
+ nextLogFile,
508
+ saveLogs,
556
509
  staleLogFiles,
557
510
  removeStaleLogs,
558
- formatSize,
559
- parseScriptLine,
560
511
  saveScript,
561
512
  removeScript,
562
513
  reorderScript,
563
514
  startNpm,
564
515
  commandEnv,
565
516
  isPassingTest,
566
- withoutNodeFrame,
567
- indentStack,
517
+ traceLine,
568
518
  relativize,
569
519
  TABLE_KEY,
570
520
  TABLE_VAL,
521
+ formatTable,
571
522
  };