@vzn/vx 0.0.51 → 0.0.53
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 +5 -5
- package/src/cli/run.ts +7 -3
- package/src/cli/select.ts +3 -0
- package/src/cli/watch.ts +3 -2
- package/src/index.ts +1 -8
- package/src/orchestrator/options.ts +8 -1
- package/src/orchestrator/prepare.ts +27 -10
- package/src/workspace/config-cache.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.53",
|
|
4
4
|
"description": "An open, extensible monorepo task runner.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.53",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.53",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.53",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.53"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/run.ts
CHANGED
|
@@ -437,9 +437,10 @@ export async function resolveRunOptions(
|
|
|
437
437
|
// changed-only `[<base>]` (item 287). `--filter '[<base>]'` stays the
|
|
438
438
|
// "only what I touched" form. Merging it into the filter list means the
|
|
439
439
|
// same code path handles plain filter use, --affected alone, and the combo.
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
// (item 955
|
|
440
|
+
// Its place does not change the selection: `applyFilters` takes every
|
|
441
|
+
// include before any exclude (item 979), so `--affected --filter '!app'`
|
|
442
|
+
// drops app from either side (item 955 had put it first for that). First
|
|
443
|
+
// it stays, so a message naming the filters names it first.
|
|
443
444
|
const filterStrings = [...parsed.filters]
|
|
444
445
|
if (parsed.affected !== undefined) {
|
|
445
446
|
const root = await findWorkspaceRoot(cwd)
|
|
@@ -461,6 +462,7 @@ export async function resolveRunOptions(
|
|
|
461
462
|
const anchoredTasks = tasks.filter((t) => t.includes('#'))
|
|
462
463
|
let projects: string[] | undefined
|
|
463
464
|
let staged: RunOptions['staged']
|
|
465
|
+
let discovered: RunOptions['discovered']
|
|
464
466
|
let selectedByDiff = false
|
|
465
467
|
if (bareTasks.length === 0) {
|
|
466
468
|
projects = undefined
|
|
@@ -485,6 +487,7 @@ export async function resolveRunOptions(
|
|
|
485
487
|
// The graph walk staged every config; the run reuses those entries
|
|
486
488
|
// instead of evaluating and staging them a second time.
|
|
487
489
|
staged = resolved.staged
|
|
490
|
+
discovered = resolved.discovered
|
|
488
491
|
}
|
|
489
492
|
} else if (parsed.all) {
|
|
490
493
|
projects = undefined
|
|
@@ -519,6 +522,7 @@ export async function resolveRunOptions(
|
|
|
519
522
|
if (projects !== undefined) opts.projects = projects
|
|
520
523
|
if (selectedByDiff) opts.selectedByDiff = true
|
|
521
524
|
if (staged !== undefined) opts.staged = staged
|
|
525
|
+
if (discovered !== undefined) opts.discovered = discovered
|
|
522
526
|
if (parsed.retries !== undefined) opts.retries = parsed.retries
|
|
523
527
|
if (parsed.timeout !== undefined) opts.timeout = parsed.timeout
|
|
524
528
|
if (parsed.cacheDir !== undefined) opts.cacheDir = parsed.cacheDir
|
package/src/cli/select.ts
CHANGED
|
@@ -152,6 +152,8 @@ export type FilterResolution =
|
|
|
152
152
|
byDiff: boolean
|
|
153
153
|
/** The staged load the graph walk needed, for the run to reuse (`RunOptions.staged`). */
|
|
154
154
|
staged?: ReadonlyMap<string, ProjectEntry>
|
|
155
|
+
/** The discovery this pass made, for the run to reuse (`RunOptions.discovered`). */
|
|
156
|
+
discovered: { root: string; projects: ProjectMeta[] }
|
|
155
157
|
}
|
|
156
158
|
| { error: string }
|
|
157
159
|
| { empty: string }
|
|
@@ -317,6 +319,7 @@ export async function resolveFilters(
|
|
|
317
319
|
names: [...selected].sort(),
|
|
318
320
|
byDiff: parsed.some((f) => !f.negate && f.gitSince !== undefined),
|
|
319
321
|
...(staged !== undefined ? { staged } : {}),
|
|
322
|
+
discovered: { root, projects },
|
|
320
323
|
}
|
|
321
324
|
}
|
|
322
325
|
|
package/src/cli/watch.ts
CHANGED
|
@@ -466,9 +466,10 @@ export async function watchCmd(args: readonly string[]): Promise<number> {
|
|
|
466
466
|
// run()'s process.argv fallback put the bin's absolute path there, so
|
|
467
467
|
// `vx last --list` showed `$ /…/bin.ts watch build` beside `$ vx run build`.
|
|
468
468
|
opts.command = ['vx', 'watch', ...args].join(' ')
|
|
469
|
-
// A staged load from the selection pass
|
|
470
|
-
// every cycle after an edit must
|
|
469
|
+
// A staged load and a discovery from the selection pass are one run's
|
|
470
|
+
// worth; every cycle after an edit must load and list live.
|
|
471
471
|
delete opts.staged
|
|
472
|
+
delete opts.discovered
|
|
472
473
|
process.once('SIGINT', () => {
|
|
473
474
|
process.stdout.write('\nvx watch: stopped\n')
|
|
474
475
|
stop.abort('SIGINT')
|
package/src/index.ts
CHANGED
|
@@ -15,9 +15,6 @@ export { VERSION } from './version.js'
|
|
|
15
15
|
// fractional SQL LIMIT is a datatype mismatch, not a smaller page) should
|
|
16
16
|
// have one implementation.
|
|
17
17
|
export { clampInt, UserError, isUserError } from './util/index.js'
|
|
18
|
-
// "Did you mean": the hint core's own verbs give for a near-miss name, for a
|
|
19
|
-
// plugin verb to give the same one.
|
|
20
|
-
export { nearMatches } from './util/index.js'
|
|
21
18
|
// Every hook a plugin may fill, in pipeline order — the one list the
|
|
22
19
|
// loader, the host and `vx info` read; exported so a doc pin outside core
|
|
23
20
|
// (the site's guides) holds its tables to the same list.
|
|
@@ -116,7 +113,6 @@ export type { ProjectEntry } from './workspace/index.js'
|
|
|
116
113
|
export {
|
|
117
114
|
applyMigration,
|
|
118
115
|
foldScriptHooks,
|
|
119
|
-
PERSISTENT_TASK_NAMES,
|
|
120
116
|
PERSISTENT_TODO,
|
|
121
117
|
quoteTsLiteral,
|
|
122
118
|
} from './workspace/index.js'
|
|
@@ -184,18 +180,15 @@ export type {
|
|
|
184
180
|
// that added them found: with only the raw `TaskStatus` union exported, every
|
|
185
181
|
// consumer rolls its own Set of status literals — and a Set has no
|
|
186
182
|
// compile-time tripwire when the union gains a member, so it silently answers
|
|
187
|
-
// "no" for the new one.
|
|
188
|
-
// consumer that needs the list rather than the predicate.
|
|
183
|
+
// "no" for the new one.
|
|
189
184
|
// `escapeMarkdownCell` is on the façade for the same demonstrated need: a
|
|
190
185
|
// plugin that renders a run as a markdown table takes the same unvalidated
|
|
191
186
|
// task names core does, and the cloud job summary shipped without the escape.
|
|
192
187
|
export {
|
|
193
188
|
escapeMarkdownCell,
|
|
194
189
|
TELEMETRY_SCHEMA_VERSION,
|
|
195
|
-
deriveCacheSource,
|
|
196
190
|
isCacheHit,
|
|
197
191
|
isPassStatus,
|
|
198
|
-
TASK_STATUSES,
|
|
199
192
|
} from './orchestrator/index.js'
|
|
200
193
|
// `TaskLogBuffer` is on the façade on the same demonstrated need: EVERY
|
|
201
194
|
// telemetry sink that ships build output has to bound it, and the retention
|
|
@@ -8,7 +8,7 @@ import type { TaskOutcome } from '../graph/index.js'
|
|
|
8
8
|
import type { EventBus } from './events.js'
|
|
9
9
|
import type { TelemetrySink } from './telemetry.js'
|
|
10
10
|
import type { Logger } from './logger.js'
|
|
11
|
-
import type { ProjectEntry } from '../workspace/index.js'
|
|
11
|
+
import type { ProjectEntry, ProjectMeta } from '../workspace/index.js'
|
|
12
12
|
|
|
13
13
|
export interface RunOptions {
|
|
14
14
|
cwd: string
|
|
@@ -39,6 +39,13 @@ export interface RunOptions {
|
|
|
39
39
|
* it per cycle).
|
|
40
40
|
*/
|
|
41
41
|
staged?: ReadonlyMap<string, ProjectEntry>
|
|
42
|
+
/**
|
|
43
|
+
* The projects a selection pass in this same process discovered under
|
|
44
|
+
* `root` (`--filter`, `--affected`). The run takes them instead of
|
|
45
|
+
* walking the workspace again when its root is the same. One run only,
|
|
46
|
+
* as `staged` is.
|
|
47
|
+
*/
|
|
48
|
+
discovered?: { root: string; projects: ProjectMeta[] }
|
|
42
49
|
concurrency?: number
|
|
43
50
|
/**
|
|
44
51
|
* Cache directory override (`--cache-dir <path>`). Absolute, or relative
|
|
@@ -179,7 +179,10 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
|
|
|
179
179
|
log.status(m),
|
|
180
180
|
)
|
|
181
181
|
mark('workspace config')
|
|
182
|
-
const projectMetas =
|
|
182
|
+
const projectMetas =
|
|
183
|
+
options.discovered?.root === workspaceRoot
|
|
184
|
+
? options.discovered.projects
|
|
185
|
+
: await listProjects(workspace)
|
|
183
186
|
mark('discover projects')
|
|
184
187
|
|
|
185
188
|
// SCOPED config loading: configs are programs, and evaluating 1090
|
|
@@ -291,14 +294,20 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
|
|
|
291
294
|
|
|
292
295
|
const requested = expandRequested(options.tasks, candidateProjects, projects)
|
|
293
296
|
let unresolvedTasks = unresolvedRequests(options.tasks, candidateProjects, projects)
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
unresolvedTasks =
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
if (options.selectedByDiff === true && unresolvedTasks.some((t) => !t.includes('#'))) {
|
|
298
|
+
// A name a loaded project declares is no typo, whether or not the load
|
|
299
|
+
// was whole: an unaffected dependency loaded for the closure declared
|
|
300
|
+
// it, and the run said no project did (C-3). Only what is still
|
|
301
|
+
// unjudged pays for the rest of the workspace.
|
|
302
|
+
unresolvedTasks = undeclaredIn(unresolvedTasks, projects)
|
|
303
|
+
if (
|
|
304
|
+
projects.size < projectsWithConfigs.length &&
|
|
305
|
+
unresolvedTasks.some((t) => !t.includes('#'))
|
|
306
|
+
) {
|
|
307
|
+
unresolvedTasks = await declaredNowhere(unresolvedTasks, () =>
|
|
308
|
+
loadProjects({ ...loadArgs, seeds: 'all', closure: false, staged: projects }),
|
|
309
|
+
)
|
|
310
|
+
}
|
|
302
311
|
}
|
|
303
312
|
|
|
304
313
|
// Cache seam precedence: an EXPLICITLY injected remote layer
|
|
@@ -527,8 +536,16 @@ async function declaredNowhere(
|
|
|
527
536
|
} catch {
|
|
528
537
|
return [...unresolved]
|
|
529
538
|
}
|
|
539
|
+
return undeclaredIn(unresolved, all.projects)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** The bare names in `unresolved` no project in `projects` declares; `a#b` forms pass through. */
|
|
543
|
+
function undeclaredIn(
|
|
544
|
+
unresolved: readonly string[],
|
|
545
|
+
projects: LoadedProjects['projects'],
|
|
546
|
+
): string[] {
|
|
530
547
|
const declared = new Set<string>()
|
|
531
|
-
for (const p of
|
|
548
|
+
for (const p of projects.values()) {
|
|
532
549
|
for (const t of Object.keys(p.config.tasks ?? {})) declared.add(t)
|
|
533
550
|
}
|
|
534
551
|
return unresolved.filter((t) => t.includes('#') || !declared.has(t))
|
|
@@ -201,8 +201,6 @@ export const PURE_CORE_EXPORTS: ReadonlySet<string> = new Set([
|
|
|
201
201
|
'normalizeGlob',
|
|
202
202
|
'isLiteralPattern',
|
|
203
203
|
'PLUGIN_HOOKS',
|
|
204
|
-
'TASK_STATUSES',
|
|
205
|
-
'PERSISTENT_TASK_NAMES',
|
|
206
204
|
])
|
|
207
205
|
|
|
208
206
|
/** Whether an `import`/`export … from '@vzn/vx'` statement takes only pure values. */
|