@vzn/vx 0.0.645 → 0.0.646

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 (71) hide show
  1. package/README.md +10 -28
  2. package/package.json +5 -5
  3. package/schemas/last.json +15 -0
  4. package/skills/vx/SKILL.md +1 -1
  5. package/src/bin.ts +1 -1
  6. package/src/cache/cache.ts +66 -16
  7. package/src/cache/git-inputs.ts +55 -10
  8. package/src/cache/index.ts +4 -0
  9. package/src/cache/inputs.ts +61 -11
  10. package/src/cache/layer.ts +25 -2
  11. package/src/cache/layered-cache.ts +32 -2
  12. package/src/cache/output-index.ts +25 -11
  13. package/src/cache/remote-lane.ts +13 -4
  14. package/src/cache/run-history.ts +13 -6
  15. package/src/cache/schema.ts +19 -2
  16. package/src/cli/core-alias.ts +5 -2
  17. package/src/cli/help.ts +4 -6
  18. package/src/cli/index.ts +0 -12
  19. package/src/cli/init.ts +43 -10
  20. package/src/cli/last.ts +4 -2
  21. package/src/cli/plugin-commands.ts +96 -3
  22. package/src/cli/plugin-templates.ts +1 -1
  23. package/src/cli/run.ts +2 -7
  24. package/src/cli/select.ts +5 -3
  25. package/src/cli/upgrade.ts +4 -5
  26. package/src/exec/executor.ts +112 -9
  27. package/src/exec/index.ts +2 -0
  28. package/src/exec/proc-sample.ts +67 -5
  29. package/src/exec/runner.ts +4 -0
  30. package/src/exec/sandbox-runtime.ts +11 -4
  31. package/src/graph/scheduler.ts +59 -8
  32. package/src/history/history.ts +119 -7
  33. package/src/history/metrics.ts +53 -16
  34. package/src/index.ts +15 -0
  35. package/src/orchestrator/execute-args.ts +3 -0
  36. package/src/orchestrator/execute-task.ts +28 -7
  37. package/src/orchestrator/held-servers.ts +86 -0
  38. package/src/orchestrator/hit-restore.ts +11 -4
  39. package/src/orchestrator/index.ts +17 -1
  40. package/src/orchestrator/local-floor.ts +104 -0
  41. package/src/orchestrator/local-shortcircuit.ts +26 -7
  42. package/src/orchestrator/miss-save.ts +10 -4
  43. package/src/orchestrator/persistent-task.ts +14 -0
  44. package/src/orchestrator/placement.ts +84 -9
  45. package/src/orchestrator/plugin-host.ts +21 -3
  46. package/src/orchestrator/plugin.ts +37 -0
  47. package/src/orchestrator/prepare.ts +11 -3
  48. package/src/orchestrator/run-keys.ts +164 -0
  49. package/src/orchestrator/run-records.ts +6 -0
  50. package/src/orchestrator/run.ts +93 -6
  51. package/src/orchestrator/sandbox-request.ts +9 -3
  52. package/src/orchestrator/task-artifacts.ts +142 -0
  53. package/src/orchestrator/task-hash.ts +11 -1
  54. package/src/orchestrator/task-process.ts +16 -0
  55. package/src/orchestrator/telemetry-host.ts +2 -1
  56. package/src/orchestrator/telemetry.ts +60 -17
  57. package/src/report/forecast.ts +6 -27
  58. package/src/report/framed-output.ts +41 -5
  59. package/src/report/logger.ts +60 -20
  60. package/src/report/status-line.ts +38 -18
  61. package/src/report/summary.ts +61 -7
  62. package/src/settings.ts +7 -0
  63. package/src/util/colors.ts +16 -1
  64. package/src/util/index.ts +1 -0
  65. package/src/util/paths.ts +30 -0
  66. package/src/util/ui.ts +4 -1
  67. package/src/workspace/config-bytecode.ts +276 -0
  68. package/src/workspace/index.ts +0 -1
  69. package/src/workspace/migration.ts +4 -32
  70. package/src/workspace/project-loader.ts +92 -14
  71. package/src/cli/foreign-flags.ts +0 -727
@@ -1,727 +0,0 @@
1
- // What a Turbo, Nx or Vite Task (`vp run`) user's hands type, and what `vx run` does with it.
2
- // Every flag here has one outcome: vx takes it as it is (`same`), takes it
3
- // as another spelling (`alias`, rewritten before the parse), or refuses it
4
- // with the vx way to say it (`refuse`). Nothing is dropped in silence: a
5
- // flag vx does not know was already a refusal, but one that named no way
6
- // on left a `turbo run --dry-run` user at a dead end. The table renders
7
- // cli.md's parity section (`renderForeignFlags`), pinned by a test.
8
-
9
- interface ForeignFlag {
10
- runner: 'turbo' | 'nx' | 'vp'
11
- /** Every spelling, first the one shown. */
12
- names: readonly string[]
13
- /** It takes a value: `--flag <v>` or `--flag=<v>`. */
14
- value: boolean
15
- /** When only some values are foreign (`--continue=dependencies-successful`). */
16
- when?: (value: string | undefined) => boolean
17
- /** How the table shows a `when` row. */
18
- shown?: string
19
- outcome: 'same' | 'alias' | 'refuse'
20
- /** The vx spelling, or what to use instead. */
21
- vx: string
22
- /**
23
- * The runner reads `=true` / `=false` on it (Turbo's `--force [FORCE]`,
24
- * Nx's yargs booleans): `=false` is the flag left out, `=true` the flag
25
- * bare: `--skip-nx-cache=false` forced every task.
26
- */
27
- bool?: true
28
- /** For an alias: the vx argv. For a refusal: none. */
29
- to?: (value: string | undefined) => string[]
30
- }
31
-
32
- const list = (v: string | undefined): string[] =>
33
- (v ?? '')
34
- .split(',')
35
- .map((s) => s.trim())
36
- .filter((s) => s !== '')
37
-
38
- // Nx's `--projects` labels (find-matching-projects, 23.2.1): `name:` and
39
- // `directory:` say which match to make; vx's filter says it by form. `tag:`
40
- // is a filter form already.
41
- const nxPattern = (p: string): string => {
42
- const neg = p.startsWith('!') ? '!' : ''
43
- const s = neg === '' ? p : p.slice(1)
44
- if (s.startsWith('name:')) return `${neg}${s.slice(5)}`
45
- if (s.startsWith('directory:')) return `${neg}./${s.slice(10)}`
46
- return p
47
- }
48
-
49
- export const FOREIGN_FLAGS: readonly ForeignFlag[] = [
50
- {
51
- runner: 'turbo',
52
- names: ['--filter'],
53
- value: true,
54
- outcome: 'same',
55
- vx: '`--filter`, the same grammar (`...[ref]`, `{dir}`, `^`, `!`)',
56
- },
57
- {
58
- runner: 'turbo',
59
- names: ['-F'],
60
- value: true,
61
- outcome: 'alias',
62
- vx: '`--filter <v>`',
63
- // Bare, it is `--filter` bare, whose refusal asks for the value.
64
- to: (v) => (v === undefined ? ['--filter'] : ['--filter', v]),
65
- },
66
- {
67
- runner: 'turbo',
68
- names: ['--concurrency'],
69
- value: true,
70
- outcome: 'same',
71
- vx: '`--concurrency <n|n%>`',
72
- },
73
- {
74
- runner: 'turbo',
75
- names: ['--continue'],
76
- value: true,
77
- when: (v) => v === 'dependencies-successful',
78
- shown: '`--continue=dependencies-successful`',
79
- outcome: 'alias',
80
- vx: '`--continue=deps-ok`',
81
- to: () => ['--continue=deps-ok'],
82
- },
83
- {
84
- runner: 'turbo',
85
- names: ['--continue'],
86
- value: false,
87
- outcome: 'same',
88
- vx: '`--continue[=never|deps-ok|always]`',
89
- },
90
- {
91
- runner: 'turbo',
92
- names: ['--dry-run'],
93
- value: false,
94
- outcome: 'alias',
95
- vx: '`--dry[=text|json]`',
96
- to: (v) => [v === undefined ? '--dry' : `--dry=${v}`],
97
- },
98
- {
99
- runner: 'turbo',
100
- names: ['--graph'],
101
- value: false,
102
- outcome: 'refuse',
103
- vx: '`--dry=json` prints the planned task graph, each task with its deps',
104
- },
105
- {
106
- runner: 'turbo',
107
- names: ['--force'],
108
- value: false,
109
- bool: true,
110
- outcome: 'same',
111
- vx: '`--force`: skip cache reads, keep writes',
112
- },
113
- {
114
- runner: 'turbo',
115
- names: ['--affected'],
116
- value: false,
117
- outcome: 'same',
118
- vx: '`--affected[=<base>]`',
119
- },
120
- {
121
- runner: 'turbo',
122
- names: ['--summarize'],
123
- value: false,
124
- outcome: 'refuse',
125
- vx: "use `vx last --format json` for the run's JSON record",
126
- },
127
- {
128
- runner: 'turbo',
129
- names: ['--output-logs'],
130
- value: true,
131
- when: (v) => v === 'new-only',
132
- shown: '`--output-logs=new-only`',
133
- outcome: 'refuse',
134
- vx: 'use `--output-logs=full` (a hit replays its log) or `errors-only`',
135
- },
136
- {
137
- runner: 'turbo',
138
- names: ['--output-logs'],
139
- value: true,
140
- outcome: 'same',
141
- vx: '`--output-logs full|errors-only|hash-only|none`',
142
- },
143
- {
144
- runner: 'turbo',
145
- names: ['--no-cache'],
146
- value: false,
147
- outcome: 'same',
148
- // Turbo 2.11's own help: "Equivalent to `--cache=local:r,remote:r`".
149
- vx: "`--no-cache` reads nothing either; Turbo's (reads, no writes) is `--cache-policy local:r,remote:r`",
150
- },
151
- {
152
- runner: 'turbo',
153
- names: ['--cache'],
154
- value: true,
155
- outcome: 'alias',
156
- vx: '`--cache-policy`, the same spec (`local:rw,remote:r`)',
157
- to: (v) => ['--cache-policy', v ?? ''],
158
- },
159
- {
160
- runner: 'turbo',
161
- names: ['--cache-dir'],
162
- value: true,
163
- outcome: 'same',
164
- vx: '`--cache-dir <path>`',
165
- },
166
- {
167
- runner: 'turbo',
168
- names: ['--profile'],
169
- value: false,
170
- outcome: 'same',
171
- vx: '`--profile[=<path>]` (Chrome trace)',
172
- },
173
- {
174
- runner: 'turbo',
175
- names: ['--only'],
176
- value: false,
177
- outcome: 'alias',
178
- vx: '`--exclude-dependencies`',
179
- to: () => ['--exclude-dependencies'],
180
- },
181
- {
182
- runner: 'turbo',
183
- names: ['--color'],
184
- value: false,
185
- outcome: 'refuse',
186
- vx: 'set `FORCE_COLOR=1`',
187
- },
188
- {
189
- runner: 'turbo',
190
- names: ['--no-color'],
191
- value: false,
192
- outcome: 'refuse',
193
- vx: 'set `NO_COLOR=1`',
194
- },
195
- {
196
- runner: 'turbo',
197
- names: ['--heap', '--trace'],
198
- value: true,
199
- outcome: 'refuse',
200
- vx: "use `--profile[=<path>]` for vx's own trace",
201
- },
202
- {
203
- runner: 'turbo',
204
- names: ['--login'],
205
- value: true,
206
- outcome: 'refuse',
207
- vx: 'vx has no login: a remote cache is a plugin (`turboCache()` from @vzn/vx-migrate)',
208
- },
209
- {
210
- runner: 'turbo',
211
- names: ['--no-update-notifier'],
212
- value: false,
213
- outcome: 'refuse',
214
- vx: 'vx prints no update notice: drop it',
215
- },
216
- {
217
- runner: 'turbo',
218
- names: ['--skip-infer'],
219
- value: false,
220
- outcome: 'refuse',
221
- vx: 'vx runs the binary it is: drop it',
222
- },
223
- {
224
- runner: 'turbo',
225
- names: ['--root-turbo-json'],
226
- value: true,
227
- outcome: 'refuse',
228
- vx: '`turbo()` reads the `turbo.json` at the workspace root: move it there',
229
- },
230
- {
231
- runner: 'turbo',
232
- names: [
233
- '--experimental-otel-enabled',
234
- '--experimental-otel-protocol',
235
- '--experimental-otel-endpoint',
236
- '--experimental-otel-timeout-ms',
237
- '--experimental-otel-interval-ms',
238
- '--experimental-otel-header',
239
- '--experimental-otel-resource',
240
- '--experimental-otel-metrics-run-summary',
241
- '--experimental-otel-metrics-task-details',
242
- '--experimental-otel-use-remote-cache-token',
243
- ],
244
- value: true,
245
- shown: '`--experimental-otel-*`',
246
- outcome: 'refuse',
247
- vx: 'telemetry is a plugin: `otel()` from @vzn/vx-otel in .vx/workspace.ts',
248
- },
249
- {
250
- runner: 'nx',
251
- names: ['--parallel'],
252
- value: true,
253
- // Nx's value is a count or `false`; a word after it is a task name.
254
- when: (v) => v === 'false' || (v !== undefined && /^\d+$/.test(v)),
255
- shown: '`--parallel <n>`',
256
- outcome: 'alias',
257
- vx: '`--concurrency <n>` (`--parallel=false` is 1)',
258
- to: (v) => ['--concurrency', v === 'false' ? '1' : (v ?? '')],
259
- },
260
- {
261
- runner: 'turbo',
262
- names: ['--parallel'],
263
- value: false,
264
- when: (v) => v === undefined || v === 'true',
265
- shown: '`--parallel`',
266
- outcome: 'refuse',
267
- vx: 'vx always honours `dependsOn`; `--concurrency <n>` sets how many run at once',
268
- },
269
- {
270
- runner: 'turbo',
271
- names: ['--scope'],
272
- value: true,
273
- outcome: 'refuse',
274
- vx: 'use `--filter <pkg>`',
275
- },
276
- {
277
- runner: 'turbo',
278
- names: ['--since'],
279
- value: true,
280
- outcome: 'refuse',
281
- vx: "use `--filter '[<ref>]'` or `--affected=<ref>`",
282
- },
283
- {
284
- runner: 'turbo',
285
- names: ['--remote-only'],
286
- value: false,
287
- bool: true,
288
- outcome: 'refuse',
289
- vx: 'use `--cache-policy local:,remote:rw`',
290
- },
291
- {
292
- runner: 'turbo',
293
- names: ['--remote-cache-read-only'],
294
- value: false,
295
- bool: true,
296
- outcome: 'refuse',
297
- vx: 'use `--cache-policy local:rw,remote:r`',
298
- },
299
- {
300
- runner: 'turbo',
301
- names: ['--anon-profile'],
302
- value: false,
303
- outcome: 'refuse',
304
- vx: 'use `--profile[=<path>]`; vx has no redacting variant, so read it before sharing it',
305
- },
306
- {
307
- runner: 'turbo',
308
- names: ['--cache-workers'],
309
- value: true,
310
- outcome: 'refuse',
311
- vx: 'vx sizes its own cache I/O: drop it',
312
- },
313
- {
314
- runner: 'turbo',
315
- names: ['--cwd'],
316
- value: true,
317
- outcome: 'refuse',
318
- vx: 'run vx from that directory: `cd <dir> && vx run …`',
319
- },
320
- {
321
- runner: 'turbo',
322
- names: ['--dangerously-disable-package-manager-check'],
323
- value: false,
324
- outcome: 'refuse',
325
- vx: 'vx reads no `packageManager` field: drop it',
326
- },
327
- {
328
- runner: 'turbo',
329
- names: ['--env-mode'],
330
- value: true,
331
- outcome: 'refuse',
332
- vx: 'vx passes only the variables a task declares (strict): list the rest in `exec.env.passThrough`',
333
- },
334
- {
335
- runner: 'turbo',
336
- names: ['--framework-inference'],
337
- value: true,
338
- outcome: 'refuse',
339
- vx: "under `turbo()` inference is Turbo's; take a name back with a `!` entry in the task's `env`",
340
- },
341
- {
342
- runner: 'turbo',
343
- names: ['--global-deps'],
344
- value: true,
345
- outcome: 'refuse',
346
- vx: "declare them in `inputs.workspaceFiles` (under `turbo()`, turbo.json's `globalDependencies`)",
347
- },
348
- {
349
- runner: 'turbo',
350
- names: ['--json'],
351
- value: false,
352
- outcome: 'refuse',
353
- vx: "use `vx last --format json` for the run's result, `--dry=json` for the plan",
354
- },
355
- {
356
- runner: 'turbo',
357
- names: ['--log-file'],
358
- value: false,
359
- outcome: 'refuse',
360
- vx: "use `vx last --format json` for the run's JSON record",
361
- },
362
- {
363
- runner: 'turbo',
364
- names: ['--preflight'],
365
- value: false,
366
- outcome: 'refuse',
367
- vx: 'set `turboCache({ preflight: true })` or `TURBO_PREFLIGHT=1`',
368
- },
369
- {
370
- runner: 'turbo',
371
- names: ['--remote-cache-timeout'],
372
- value: true,
373
- outcome: 'refuse',
374
- vx: 'set `turboCache({ timeoutMs })` or `TURBO_REMOTE_CACHE_TIMEOUT`',
375
- },
376
- {
377
- runner: 'turbo',
378
- names: ['--single-package'],
379
- value: false,
380
- outcome: 'refuse',
381
- vx: 'a repo with no workspaces is one project already: drop it',
382
- },
383
- {
384
- runner: 'turbo',
385
- names: ['--token', '--team', '--api'],
386
- value: true,
387
- outcome: 'refuse',
388
- vx: 'a remote cache is a plugin: `turboCache()` from @vzn/vx-migrate in .vx/workspace.ts reads TURBO_TOKEN / TURBO_TEAM / TURBO_API',
389
- },
390
- {
391
- runner: 'turbo',
392
- names: ['--no-daemon', '--daemon'],
393
- value: false,
394
- outcome: 'refuse',
395
- vx: 'vx has no daemon: drop it',
396
- },
397
- {
398
- runner: 'turbo',
399
- names: ['--ui', '--log-order', '--log-prefix'],
400
- value: true,
401
- outcome: 'refuse',
402
- vx: 'vx frames each task’s output: `--output-logs <mode>` sets how much',
403
- },
404
- {
405
- runner: 'nx',
406
- names: ['-t', '--targets', '--target'],
407
- value: true,
408
- outcome: 'alias',
409
- vx: 'the task names, positional: `vx run build test`',
410
- to: (v) => list(v),
411
- },
412
- {
413
- runner: 'nx',
414
- names: ['-p', '--projects'],
415
- value: true,
416
- outcome: 'alias',
417
- vx: "`--filter <pattern>`, one per project (`directory:<d>` is `./<d>`, `name:<n>` is `<n>`); a list opening with `!` starts from all (`--filter '*'`)",
418
- // Nx reads a leading exclusion as "all projects but": `-p '!b,a'` is
419
- // every project except b, where vx's filters alone would select a.
420
- to: (v) => {
421
- const ps = list(v)
422
- return [...(ps[0]?.startsWith('!') ? ['*'] : []), ...ps].flatMap((p) => [
423
- '--filter',
424
- nxPattern(p),
425
- ])
426
- },
427
- },
428
- {
429
- runner: 'nx',
430
- names: ['--exclude'],
431
- value: true,
432
- outcome: 'alias',
433
- vx: "`--filter '!<pattern>'`, one per project",
434
- to: (v) => list(v).flatMap((p) => ['--filter', `!${nxPattern(p)}`]),
435
- },
436
- {
437
- runner: 'nx',
438
- names: ['--base'],
439
- value: true,
440
- outcome: 'alias',
441
- vx: '`--affected=<ref>`',
442
- to: (v) => [`--affected=${v ?? ''}`],
443
- },
444
- {
445
- runner: 'nx',
446
- names: ['--head'],
447
- value: true,
448
- when: (v) => v === 'HEAD',
449
- shown: '`--head HEAD`',
450
- outcome: 'alias',
451
- vx: 'nothing: vx compares `--affected=<base>` with the working tree',
452
- to: () => [],
453
- },
454
- {
455
- runner: 'nx',
456
- names: ['--head'],
457
- value: true,
458
- outcome: 'refuse',
459
- vx: 'vx compares `--affected=<base>` with the working tree: check out the head first',
460
- },
461
- {
462
- runner: 'nx',
463
- names: ['--skip-nx-cache'],
464
- value: false,
465
- bool: true,
466
- outcome: 'alias',
467
- vx: '`--force`',
468
- to: () => ['--force'],
469
- },
470
- { runner: 'nx', names: ['--all'], value: false, outcome: 'same', vx: '`--all`' },
471
- {
472
- runner: 'nx',
473
- names: ['--nx-bail'],
474
- value: false,
475
- bool: true,
476
- outcome: 'alias',
477
- vx: '`--continue=never`',
478
- to: () => ['--continue=never'],
479
- },
480
- {
481
- runner: 'nx',
482
- names: ['-c', '--configuration'],
483
- value: true,
484
- outcome: 'refuse',
485
- vx: 'a configuration is its own task: `vx run <target>:<configuration>`',
486
- },
487
- {
488
- runner: 'nx',
489
- names: ['--output-style'],
490
- value: true,
491
- outcome: 'refuse',
492
- vx: 'use `--output-logs <mode>`',
493
- },
494
- {
495
- runner: 'nx',
496
- names: ['--uncommitted', '--untracked'],
497
- value: false,
498
- outcome: 'refuse',
499
- vx: 'use `--affected=HEAD` (the working tree against the last commit)',
500
- },
501
- {
502
- runner: 'nx',
503
- names: ['--max-parallel'],
504
- value: true,
505
- outcome: 'alias',
506
- vx: '`--concurrency <n>`',
507
- to: (v) => (v === undefined ? ['--concurrency'] : ['--concurrency', v]),
508
- },
509
- {
510
- runner: 'nx',
511
- names: ['--exclude-task-dependencies'],
512
- value: false,
513
- bool: true,
514
- outcome: 'alias',
515
- vx: '`--exclude-dependencies`',
516
- to: () => ['--exclude-dependencies'],
517
- },
518
- {
519
- runner: 'nx',
520
- names: ['--skip-remote-cache'],
521
- value: false,
522
- bool: true,
523
- outcome: 'alias',
524
- vx: '`--cache-policy local:rw,remote:`',
525
- to: () => ['--cache-policy', 'local:rw,remote:'],
526
- },
527
- {
528
- runner: 'nx',
529
- names: ['--verbose'],
530
- value: false,
531
- outcome: 'refuse',
532
- vx: 'use `--verbosity <n>` (1 adds the summary table)',
533
- },
534
- {
535
- runner: 'nx',
536
- names: ['--files'],
537
- value: true,
538
- outcome: 'refuse',
539
- vx: 'vx asks git what changed: `--affected=<base>`',
540
- },
541
- {
542
- runner: 'nx',
543
- names: ['--batch'],
544
- value: false,
545
- outcome: 'refuse',
546
- vx: 'vx runs one command per task: drop it',
547
- },
548
- {
549
- runner: 'nx',
550
- names: ['--dte', '--use-agents'],
551
- value: false,
552
- outcome: 'refuse',
553
- vx: 'vx distributes nothing: drop it',
554
- },
555
- {
556
- runner: 'nx',
557
- names: ['--nx-ignore-cycles'],
558
- value: false,
559
- outcome: 'refuse',
560
- vx: 'vx refuses a task cycle by name: break it',
561
- },
562
- {
563
- runner: 'nx',
564
- names: ['--runner'],
565
- value: true,
566
- outcome: 'refuse',
567
- vx: 'a remote cache is a plugin: `nxCache()` from @vzn/vx-migrate in .vx/workspace.ts',
568
- },
569
- {
570
- runner: 'nx',
571
- names: ['--skip-sync'],
572
- value: false,
573
- outcome: 'refuse',
574
- vx: 'vx never runs sync generators: drop it',
575
- },
576
- {
577
- runner: 'nx',
578
- names: ['--tui', '--no-tui', '--tui-auto-exit'],
579
- value: false,
580
- outcome: 'refuse',
581
- vx: 'vx frames each task’s output: `--output-logs <mode>` sets how much',
582
- },
583
- {
584
- runner: 'nx',
585
- names: ['--no-cloud'],
586
- value: false,
587
- outcome: 'refuse',
588
- vx: 'vx has no cloud: drop it',
589
- },
590
- {
591
- runner: 'vp',
592
- names: ['-r', '--recursive'],
593
- value: false,
594
- outcome: 'alias',
595
- vx: '`--all`',
596
- to: () => ['--all'],
597
- },
598
- {
599
- runner: 'vp',
600
- names: ['-w', '--workspace-root'],
601
- value: false,
602
- outcome: 'alias',
603
- vx: '`--filter //` (the root project)',
604
- to: () => ['--filter', '//'],
605
- },
606
- {
607
- runner: 'vp',
608
- names: ['--concurrency-limit'],
609
- value: true,
610
- outcome: 'alias',
611
- vx: '`--concurrency <n>`',
612
- to: (v) => (v === undefined ? ['--concurrency'] : ['--concurrency', v]),
613
- },
614
- {
615
- runner: 'vp',
616
- names: ['--ignore-depends-on'],
617
- value: false,
618
- outcome: 'alias',
619
- vx: '`--exclude-dependencies`',
620
- to: () => ['--exclude-dependencies'],
621
- },
622
- {
623
- runner: 'vp',
624
- names: ['--fail-if-no-match'],
625
- value: false,
626
- outcome: 'alias',
627
- vx: 'nothing: a filter that matches nothing already fails the run',
628
- to: () => [],
629
- },
630
- {
631
- runner: 'vp',
632
- names: ['--log'],
633
- value: true,
634
- outcome: 'refuse',
635
- vx: 'vx frames each task’s output: `--output-logs <mode>` sets how much',
636
- },
637
- {
638
- runner: 'vp',
639
- names: ['--last-details'],
640
- value: false,
641
- outcome: 'refuse',
642
- vx: '`vx last` prints the last run',
643
- },
644
- ]
645
-
646
- /** Nx's verbs, as its users type them: `vx run` takes their flags. */
647
- const TURBO_LOGIN =
648
- 'vx has no login: a remote cache is a plugin, `turboCache()` from @vzn/vx-migrate in .vx/workspace.ts, which reads TURBO_TOKEN / TURBO_TEAM / TURBO_API'
649
-
650
- export const FOREIGN_VERBS: Readonly<Record<string, string>> = {
651
- 'run-many':
652
- '`nx run-many` is `vx run <task> --all` here; -t, -p, --exclude and --parallel work as they are',
653
- affected:
654
- '`nx affected` is `vx run <task> --affected` here; -t, --base and --exclude work as they are',
655
- graph: '`nx graph` is `vx run <task> --dry=json` here: the planned task graph as JSON',
656
- ls: '`turbo ls` is `vx show` here: every project, its directory and its task count',
657
- query:
658
- '`turbo query` has no vx form: `vx run <task> --dry=json` prints the planned graph as JSON',
659
- reset:
660
- '`nx reset` is `vx cache clean` here; `--older-than <age>` trims the cache instead of clearing it',
661
- daemon: 'vx has no daemon: there is nothing to start or stop',
662
- login: TURBO_LOGIN,
663
- logout: TURBO_LOGIN,
664
- link: TURBO_LOGIN,
665
- unlink: TURBO_LOGIN,
666
- }
667
-
668
- /**
669
- * Rewrite each aliased foreign flag into its vx argv, or refuse the first
670
- * refused one. A `same` flag and anything unknown pass through to the
671
- * parser. Arguments after `--` are the task's and are never read.
672
- */
673
- export function translateForeign(args: readonly string[]): string[] | { error: string } {
674
- const out: string[] = []
675
- const sep = args.indexOf('--')
676
- const before = sep === -1 ? args : args.slice(0, sep)
677
- for (let i = 0; i < before.length; i++) {
678
- let a = before[i]!
679
- const eq = a.indexOf('=')
680
- let name = eq === -1 ? a : a.slice(0, eq)
681
- const inline = eq === -1 ? undefined : a.slice(eq + 1)
682
- // Nx's parser takes every long flag camelCased too (`--nxBail`,
683
- // `--skipNxCache`), and its docs print that spelling; vx refused it
684
- // as an unknown flag.
685
- const kebab = name.startsWith('--')
686
- ? name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)
687
- : name
688
- if (kebab !== name && FOREIGN_FLAGS.some((f) => f.runner === 'nx' && f.names.includes(kebab))) {
689
- name = kebab
690
- a = inline === undefined ? kebab : `${kebab}=${inline}`
691
- }
692
- if (
693
- (inline === 'true' || inline === 'false') &&
694
- FOREIGN_FLAGS.some((f) => f.bool && f.names.includes(name))
695
- ) {
696
- if (inline === 'false') continue
697
- a = name
698
- }
699
- const entries = FOREIGN_FLAGS.filter((f) => f.names.includes(name) || f.names.includes(a))
700
- const next = before[i + 1]
701
- const spaced = inline === undefined && next !== undefined && !next.startsWith('-')
702
- const hit = entries.find((f) => {
703
- const v = f.value ? (inline ?? (spaced ? next : undefined)) : inline
704
- return f.when === undefined || f.when(v)
705
- })
706
- if (hit === undefined || hit.outcome === 'same') {
707
- out.push(a)
708
- continue
709
- }
710
- const v = hit.value ? (inline ?? (spaced ? next : undefined)) : inline
711
- if (hit.value && inline === undefined && spaced) i++
712
- if (hit.outcome === 'refuse') return { error: `${name} (${hit.runner}): ${hit.vx}` }
713
- out.push(...hit.to!(v))
714
- }
715
- return sep === -1 ? out : [...out, ...args.slice(sep)]
716
- }
717
-
718
- /** cli.md's parity table, one row per entry. */
719
- export function renderForeignFlags(): string {
720
- const rows = FOREIGN_FLAGS.map((f) => {
721
- const flag =
722
- f.shown ??
723
- f.names.map((n) => `\`${n}${f.value ? ' <v>' : f.bool ? '[=<bool>]' : ''}\``).join(', ')
724
- return `| ${f.runner} | ${flag.replaceAll('|', '\\|')} | ${f.outcome} | ${f.vx.replaceAll('|', '\\|')} |`
725
- })
726
- return ['| runner | flag | outcome | in vx |', '| --- | --- | --- | --- |', ...rows].join('\n')
727
- }