@vzn/vx 0.0.186 → 0.0.188

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.186",
3
+ "version": "0.0.188",
4
4
  "description": "A fast, correct task runner and build cache for JavaScript monorepos.",
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.186",
21
- "@vzn/vx-linux-arm64": "0.0.186",
22
- "@vzn/vx-darwin-x64": "0.0.186",
23
- "@vzn/vx-darwin-arm64": "0.0.186"
20
+ "@vzn/vx-linux-x64": "0.0.188",
21
+ "@vzn/vx-linux-arm64": "0.0.188",
22
+ "@vzn/vx-darwin-x64": "0.0.188",
23
+ "@vzn/vx-darwin-arm64": "0.0.188"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/run.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  import type { ContinueMode } from '../graph/index.js'
17
17
  import { type CachePolicy, FULL_CACHE_POLICY, parseCachePolicy } from '../cache/index.js'
18
18
  import { findCwdProject, pickTask, resolveFilters } from './select.js'
19
+ import { nxTargetHint } from './task-verb.js'
19
20
  import { MAX_TIMEOUT_MS, isUserError, parseDecimalInt, machineParallelism } from '../util/index.js'
20
21
  import { formatGraphDot, formatPlanJson, formatPlanText } from './plan-format.js'
21
22
 
@@ -512,6 +513,8 @@ export async function resolveRunOptions(
512
513
  } else {
513
514
  const cwdProject = await findCwdProject(cwd)
514
515
  if (!cwdProject) {
516
+ const nx = await nxTargetHint(tasks, cwd)
517
+ if (nx !== null) return { error: nx }
515
518
  return {
516
519
  error:
517
520
  'not inside a project. Pass --all for every project, --filter <pattern> to filter, or run from within a project directory.',
@@ -4,6 +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
8
  import { findWorkspaceRoot, listProjects, loadWorkspace } from '../workspace/index.js'
8
9
  import { findCwdProject } from './select.js'
9
10
  import { loadCliProjects } from './workspace-config.js'
@@ -15,16 +16,8 @@ export async function taskVerbHint(
15
16
  cwd: string,
16
17
  ): Promise<string | null> {
17
18
  if (command.includes('#')) return `\`${command}\` is a task: vx run ${command}`
18
- let projects: Awaited<ReturnType<typeof loadCliProjects>>
19
- try {
20
- const root = await findWorkspaceRoot(cwd)
21
- projects = await loadCliProjects(root, await listProjects(await loadWorkspace(root)), 'all', {
22
- noCreate: true,
23
- })
24
- } catch {
25
- // No workspace, or a config that does not load: the plain unknown-command line stands.
26
- return null
27
- }
19
+ const projects = await workspaceProjects(cwd)
20
+ if (projects === null) return null
28
21
  if (![...projects.values()].some((p) => p.config.tasks?.[command] !== undefined)) return null
29
22
  // Nx's `nx build app`: the word after the target is its project.
30
23
  const project = rest[0] !== undefined && projects.has(rest[0]) ? rest[0] : undefined
@@ -36,3 +29,30 @@ export async function taskVerbHint(
36
29
  : `vx run ${command}`
37
30
  return `\`${command}\` is a task here, not a command: ${run}`
38
31
  }
32
+
33
+ /**
34
+ * `vx run web:build` outside a project, as `nx run web:build` is typed:
35
+ * the `vx run` of each name that is Nx's `project:target` here, or null.
36
+ */
37
+ export async function nxTargetHint(tasks: readonly string[], cwd: string): Promise<string | null> {
38
+ if (!tasks.some((t) => t.includes(':'))) return null
39
+ const projects = await workspaceProjects(cwd)
40
+ if (projects === null) return null
41
+ const specs = tasks.map((t) => nxProjectTarget(t, projects))
42
+ if (specs.some((s) => s === undefined)) return null
43
+ return `\`${tasks.join(' ')}\` is Nx's project:target: vx run ${specs.join(' ')}`
44
+ }
45
+
46
+ async function workspaceProjects(
47
+ cwd: string,
48
+ ): Promise<Awaited<ReturnType<typeof loadCliProjects>> | null> {
49
+ try {
50
+ const root = await findWorkspaceRoot(cwd)
51
+ return await loadCliProjects(root, await listProjects(await loadWorkspace(root)), 'all', {
52
+ noCreate: true,
53
+ })
54
+ } catch {
55
+ // No workspace, or a config that does not load: the caller's plain line stands.
56
+ return null
57
+ }
58
+ }
@@ -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 } from './run.js'
4
+ export { run, planRun, nxProjectTarget } from './run.js'
5
5
  export { prepareRun, type PreparedRun } from './prepare.js'
6
6
  export {
7
7
  loadProjects,
@@ -1298,6 +1298,11 @@ function didYouMean(
1298
1298
  const hints = new Set<string>()
1299
1299
  for (const spec of unresolved) {
1300
1300
  const at = spec.indexOf('#')
1301
+ const nx = nxProjectTarget(spec, projects)
1302
+ if (nx !== undefined) {
1303
+ hints.add(nx)
1304
+ continue
1305
+ }
1301
1306
  if (at < 0) {
1302
1307
  const t = nearest(spec, allTasks)
1303
1308
  if (t !== undefined) hints.add(t)
@@ -1315,6 +1320,23 @@ function didYouMean(
1315
1320
  return hints.size === 0 ? '' : ` Did you mean ${[...hints].join(', ')}?`
1316
1321
  }
1317
1322
 
1323
+ /**
1324
+ * Nx's `project:target` (`nx run web:build`) as vx spells it, `web#build`,
1325
+ * when that project declares that task; a name with a colon is otherwise
1326
+ * a task of its own (`build:prod`).
1327
+ */
1328
+ export function nxProjectTarget(
1329
+ spec: string,
1330
+ projects: ReadonlyMap<string, ProjectEntry>,
1331
+ ): string | undefined {
1332
+ const colon = spec.indexOf(':')
1333
+ if (colon <= 0 || spec.includes('#')) return undefined
1334
+ const [project, task] = [spec.slice(0, colon), spec.slice(colon + 1)]
1335
+ return projects.get(project)?.config.tasks?.[task] === undefined
1336
+ ? undefined
1337
+ : `${project}#${task}`
1338
+ }
1339
+
1318
1340
  /** The executed, keyed outcomes of a run — what flakiness is judged on. */
1319
1341
  function flakyCandidates(outcomes: readonly TaskOutcome[]): FlakyCandidate[] {
1320
1342
  const out: FlakyCandidate[] = []