@vzn/vx 0.0.326 → 0.0.328
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.328",
|
|
4
4
|
"description": "A fast, correct task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.328",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.328",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.328",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.328"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/foreign-flags.ts
CHANGED
|
@@ -522,10 +522,20 @@ export function translateForeign(args: readonly string[]): string[] | { error: s
|
|
|
522
522
|
const sep = args.indexOf('--')
|
|
523
523
|
const before = sep === -1 ? args : args.slice(0, sep)
|
|
524
524
|
for (let i = 0; i < before.length; i++) {
|
|
525
|
-
|
|
525
|
+
let a = before[i]!
|
|
526
526
|
const eq = a.indexOf('=')
|
|
527
|
-
|
|
527
|
+
let name = eq === -1 ? a : a.slice(0, eq)
|
|
528
528
|
const inline = eq === -1 ? undefined : a.slice(eq + 1)
|
|
529
|
+
// Nx's parser takes every long flag camelCased too (`--nxBail`,
|
|
530
|
+
// `--skipNxCache`), and its docs print that spelling; vx refused it
|
|
531
|
+
// as an unknown flag.
|
|
532
|
+
const kebab = name.startsWith('--')
|
|
533
|
+
? name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)
|
|
534
|
+
: name
|
|
535
|
+
if (kebab !== name && FOREIGN_FLAGS.some((f) => f.runner === 'nx' && f.names.includes(kebab))) {
|
|
536
|
+
name = kebab
|
|
537
|
+
a = inline === undefined ? kebab : `${kebab}=${inline}`
|
|
538
|
+
}
|
|
529
539
|
const entries = FOREIGN_FLAGS.filter((f) => f.names.includes(name) || f.names.includes(a))
|
|
530
540
|
const next = before[i + 1]
|
|
531
541
|
const spaced = inline === undefined && next !== undefined && !next.startsWith('-')
|
package/src/cli/task-verb.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// does it. It stays a refusal: a verb that runs whatever task shares its
|
|
5
5
|
// name would change meaning the day a plugin declares that verb.
|
|
6
6
|
|
|
7
|
-
import { nxProjectTarget } from '../orchestrator/index.js'
|
|
7
|
+
import { nxProjectTarget, projectNamed } from '../orchestrator/index.js'
|
|
8
8
|
import { findWorkspaceRoot, loadWorkspace } from '../workspace/index.js'
|
|
9
9
|
import { findCwdProject } from './select.js'
|
|
10
10
|
import { discoverCliProjects, loadCliProjects } from './workspace-config.js'
|
|
@@ -20,7 +20,7 @@ export async function taskVerbHint(
|
|
|
20
20
|
if (projects === null) return null
|
|
21
21
|
if (![...projects.values()].some((p) => p.config.tasks?.[command] !== undefined)) return null
|
|
22
22
|
// Nx's `nx build app`: the word after the target is its project.
|
|
23
|
-
const project = rest[0]
|
|
23
|
+
const project = rest[0] === undefined ? undefined : projectNamed(rest[0], projects)
|
|
24
24
|
const run =
|
|
25
25
|
project !== undefined
|
|
26
26
|
? `vx run ${command} --filter ${project}`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Module contract for `orchestrator`. Cross-module imports must come
|
|
2
2
|
// through here (enforced by tests/module-boundaries.test.ts).
|
|
3
3
|
|
|
4
|
-
export { run, planRun, nxProjectTarget } from './run.js'
|
|
4
|
+
export { run, planRun, nxProjectTarget, projectNamed } from './run.js'
|
|
5
5
|
export { prepareRun, type PreparedRun } from './prepare.js'
|
|
6
6
|
export {
|
|
7
7
|
discoverProjects,
|
|
@@ -111,8 +111,10 @@ export interface PreparedRun {
|
|
|
111
111
|
* selection (the cwd's project, a `--filter`) declares: no typo, a scope.
|
|
112
112
|
*/
|
|
113
113
|
declaredElsewhere: readonly string[]
|
|
114
|
-
/**
|
|
114
|
+
/** The loaded projects: the whole workspace, or a scoped run's closure. */
|
|
115
115
|
projects: ReadonlyMap<string, ProjectEntry>
|
|
116
|
+
/** What a typo is measured against: `projects`, or every project when a `pkg#task` named none loaded. */
|
|
117
|
+
hintProjects: ReadonlyMap<string, ProjectEntry>
|
|
116
118
|
/**
|
|
117
119
|
* True iff at least one package in the workspace has a `vx.config.*` at
|
|
118
120
|
* all — regardless of scope, so a `--filter` that matched nothing is not
|
|
@@ -353,6 +355,24 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
|
|
|
353
355
|
}
|
|
354
356
|
declaredElsewhere = bare.filter((t) => !nowhere.includes(t))
|
|
355
357
|
}
|
|
358
|
+
// A `pkg#task` run loads pkg's closure alone, so a typo'd pkg (or Nx's
|
|
359
|
+
// short name for `@scope/pkg`) was measured against nothing and got no
|
|
360
|
+
// "did you mean". Only a failing run pays for the rest of the workspace.
|
|
361
|
+
let hintProjects: ReadonlyMap<string, ProjectEntry> = projects
|
|
362
|
+
if (
|
|
363
|
+
unresolvedTasks.some((t) => {
|
|
364
|
+
const at = t.indexOf('#')
|
|
365
|
+
return at > 0 && !projects.has(t.slice(0, at))
|
|
366
|
+
})
|
|
367
|
+
) {
|
|
368
|
+
try {
|
|
369
|
+
hintProjects = (
|
|
370
|
+
await loadProjects({ ...loadArgs, seeds: 'all', closure: false, staged: projects })
|
|
371
|
+
).projects
|
|
372
|
+
} catch {
|
|
373
|
+
// A config the scope skipped fails to load: no hint, the run's refusal stands.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
356
376
|
|
|
357
377
|
// Cache seam precedence: an EXPLICITLY injected remote layer
|
|
358
378
|
// (RunOptions.remoteCache — a distribution agent or daemon that already
|
|
@@ -438,6 +458,7 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
|
|
|
438
458
|
unresolvedTasks,
|
|
439
459
|
declaredElsewhere,
|
|
440
460
|
projects,
|
|
461
|
+
hintProjects,
|
|
441
462
|
anyProjectConfig: projectsWithConfigs.length > 0,
|
|
442
463
|
workspaceFingerprint,
|
|
443
464
|
fingerprintWatch,
|
|
@@ -558,6 +579,7 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
|
|
|
558
579
|
unresolvedTasks,
|
|
559
580
|
declaredElsewhere,
|
|
560
581
|
projects,
|
|
582
|
+
hintProjects,
|
|
561
583
|
anyProjectConfig: projectsWithConfigs.length > 0,
|
|
562
584
|
workspaceFingerprint,
|
|
563
585
|
fingerprintWatch,
|
package/src/orchestrator/run.ts
CHANGED
|
@@ -273,7 +273,7 @@ async function runOnBus(
|
|
|
273
273
|
// case too; the message is identical, so that branch stays below.
|
|
274
274
|
if (prepared.unresolvedTasks.length > 0) {
|
|
275
275
|
log.status(
|
|
276
|
-
`No projects declare task(s): ${prepared.unresolvedTasks.join(', ')}.${elsewhereHint(prepared.declaredElsewhere)}${didYouMean(prepared.unresolvedTasks, prepared.
|
|
276
|
+
`No projects declare task(s): ${prepared.unresolvedTasks.join(', ')}.${elsewhereHint(prepared.declaredElsewhere)}${didYouMean(prepared.unresolvedTasks, prepared.hintProjects)}${await initHint(prepared)}`,
|
|
277
277
|
)
|
|
278
278
|
await teardown()
|
|
279
279
|
prepared.cache.close()
|
|
@@ -1210,7 +1210,7 @@ export async function planRun(options: RunOptions): Promise<RunPlan> {
|
|
|
1210
1210
|
return {
|
|
1211
1211
|
tasks: [],
|
|
1212
1212
|
unresolvedTasks: prepared.unresolvedTasks,
|
|
1213
|
-
unresolvedHint: `${elsewhereHint(prepared.declaredElsewhere)}${didYouMean(prepared.unresolvedTasks, prepared.
|
|
1213
|
+
unresolvedHint: `${elsewhereHint(prepared.declaredElsewhere)}${didYouMean(prepared.unresolvedTasks, prepared.hintProjects)}${await initHint(prepared)}`,
|
|
1214
1214
|
}
|
|
1215
1215
|
}
|
|
1216
1216
|
if (prepared.empty === 'no-tasks-declared' && prepared.declaredElsewhere.length > 0) {
|
|
@@ -1323,7 +1323,7 @@ function didYouMean(
|
|
|
1323
1323
|
}
|
|
1324
1324
|
const [proj, task] = [spec.slice(0, at), spec.slice(at + 1)]
|
|
1325
1325
|
if (!projects.has(proj)) {
|
|
1326
|
-
const p = nearest(proj, projects.keys())
|
|
1326
|
+
const p = projectNamed(proj, projects) ?? nearest(proj, projects.keys())
|
|
1327
1327
|
if (p !== undefined && tasksOf(projects.get(p)).includes(task)) hints.add(`${p}#${task}`)
|
|
1328
1328
|
continue
|
|
1329
1329
|
}
|
|
@@ -1344,12 +1344,28 @@ export function nxProjectTarget(
|
|
|
1344
1344
|
): string | undefined {
|
|
1345
1345
|
const colon = spec.indexOf(':')
|
|
1346
1346
|
if (colon <= 0 || spec.includes('#')) return undefined
|
|
1347
|
-
const [
|
|
1348
|
-
|
|
1347
|
+
const [typed, task] = [spec.slice(0, colon), spec.slice(colon + 1)]
|
|
1348
|
+
const project = projectNamed(typed, projects)
|
|
1349
|
+
return project === undefined || projects.get(project)?.config.tasks?.[task] === undefined
|
|
1349
1350
|
? undefined
|
|
1350
1351
|
: `${project}#${task}`
|
|
1351
1352
|
}
|
|
1352
1353
|
|
|
1354
|
+
/**
|
|
1355
|
+
* The project a typed name means: itself, else the one project whose
|
|
1356
|
+
* scoped name ends in it. Nx names nx-examples' `@nx-example/cart` `cart`,
|
|
1357
|
+
* and `nx run cart:build` is what its users type.
|
|
1358
|
+
*/
|
|
1359
|
+
export function projectNamed(
|
|
1360
|
+
typed: string,
|
|
1361
|
+
projects: ReadonlyMap<string, ProjectEntry>,
|
|
1362
|
+
): string | undefined {
|
|
1363
|
+
if (projects.has(typed)) return typed
|
|
1364
|
+
if (typed.includes('/')) return undefined
|
|
1365
|
+
const scoped = [...projects.keys()].filter((n) => n.startsWith('@') && n.endsWith(`/${typed}`))
|
|
1366
|
+
return scoped.length === 1 ? scoped[0] : undefined
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1353
1369
|
/** The executed, keyed outcomes of a run — what flakiness is judged on. */
|
|
1354
1370
|
function flakyCandidates(outcomes: readonly TaskOutcome[]): FlakyCandidate[] {
|
|
1355
1371
|
const out: FlakyCandidate[] = []
|