@vzn/vx 0.0.412 → 0.0.414

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.412",
3
+ "version": "0.0.414",
4
4
  "description": "The fastest 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.412",
25
- "@vzn/vx-linux-arm64": "0.0.412",
26
- "@vzn/vx-darwin-x64": "0.0.412",
27
- "@vzn/vx-darwin-arm64": "0.0.412"
24
+ "@vzn/vx-linux-x64": "0.0.414",
25
+ "@vzn/vx-linux-arm64": "0.0.414",
26
+ "@vzn/vx-darwin-x64": "0.0.414",
27
+ "@vzn/vx-darwin-arm64": "0.0.414"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/select.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  // cwd sits in, and the interactive picker. Every read of the workspace
4
4
  // here goes through the staged load, so the answer is the run's.
5
5
 
6
- import readline from 'node:readline/promises'
7
6
  import path from 'node:path'
8
7
  import fs from 'node:fs'
9
8
  import {
@@ -404,6 +403,9 @@ export async function pickTask(
404
403
  const desc = e.description ? ` ${e.description}` : ''
405
404
  out.write(` ${n}. ${id}${desc}\n`)
406
405
  })
406
+ // Imported here, the picker's one use: a run that never asks paid ~0.7 ms
407
+ // for it at every start, past the stdout stream it shares code with.
408
+ const readline = await import('node:readline/promises')
407
409
  const rl = readline.createInterface({
408
410
  input: io.input ?? process.stdin,
409
411
  output: io.output ?? process.stdout,
@@ -661,12 +661,15 @@ export function migrateScripts(
661
661
  // the task, so `--all` never runs one check twice.
662
662
  const outsideName =
663
663
  typeof outside?.['name'] === 'string' && outside['name'] !== '' ? outside['name'] : undefined
664
+ // insomnia's root is named as its `packages/insomnia`: a root config made
665
+ // it a project, and every later run was refused for the duplicate (D-129).
666
+ const clash = outsideName === undefined ? undefined : metas.find((m) => m.name === outsideName)
664
667
  const rootMeta: ProjectMeta | undefined =
665
668
  root !== undefined
666
669
  ? root.configPath === null
667
670
  ? root
668
671
  : undefined
669
- : outsideName !== undefined && outsideDir !== undefined
672
+ : outsideName !== undefined && outsideDir !== undefined && clash === undefined
670
673
  ? { name: outsideName, dir: outsideDir, packageJson: outside as never, configPath: null }
671
674
  : undefined
672
675
  const memberTasks = new Set(
@@ -774,6 +777,11 @@ export function migrateScripts(
774
777
  ? `; left out as a member's task name, so \`--all\` never runs one twice: ${listed(shared)} — one that does other work maps by hand under a name of its own`
775
778
  : ''),
776
779
  )
780
+ } else if (clash !== undefined && outsideDir !== undefined && unnamedMaps().length > 0) {
781
+ const would = unnamedMaps()
782
+ notes.push(
783
+ `${clash.name} (the workspace root) not mapped: ${path.relative(outsideDir, clash.dir).split(path.sep).join('/')} has the same "name", and vx names a project by it; rename the root's and run \`vx init\` again to map ${would.length} of its scripts (${would.slice(0, 8).join(', ')}${would.length > 8 ? ', …' : ''})`,
784
+ )
777
785
  } else if (rootName === 'package.json' && outsideDir !== undefined && unnamedMaps().length > 0) {
778
786
  // react's nameless root: "its scripts run the workspace" was not why,
779
787
  // and 30 of its scripts (`build`, `lint`, `test`) map once it has a
@@ -10,7 +10,7 @@ import { unlink } from 'node:fs/promises'
10
10
  import path from 'node:path'
11
11
  import { executablePath, relPosix, UserError } from '../util/index.js'
12
12
  import type { ProjectMeta } from './workspace.js'
13
- import { loadWorkspace, unreachedHint, unreachedPackages } from './workspace.js'
13
+ import { loadWorkspace, reachesManifest, unreachedHint, unreachedPackages } from './workspace.js'
14
14
  import { WORKSPACE_CONFIG_FILENAMES } from './project-loader.js'
15
15
 
16
16
  /**
@@ -357,8 +357,10 @@ export async function applyMigration(args: ApplyMigrationArgs): Promise<number>
357
357
  // Globs that reach no package: "no scripts" was true and named nothing
358
358
  // to fix (M-42).
359
359
  const globs = workspace?.packageGlobs.filter((g) => g !== '.') ?? []
360
+ // Asked of the globs, not of `metas`: a nameless or shared-name member
361
+ // is matched yet absent from `metas` (M-46).
360
362
  const noMembers =
361
- globs.length > 0 && metas.every((m) => path.resolve(m.dir) === path.resolve(root))
363
+ workspace !== undefined && globs.length > 0 && !(await reachesManifest(workspace))
362
364
  ? [
363
365
  `the workspace globs (${globs.map((g) => `"${g}"`).join(', ')}) match no package.json: add a package under one, or fix the glob`,
364
366
  ]
@@ -395,6 +395,28 @@ export function memberBaseDirs(workspace: Workspace): string[] {
395
395
  return [...bases]
396
396
  }
397
397
 
398
+ /**
399
+ * True when a member glob reaches a `package.json` other than the root's,
400
+ * addressable or not: a nameless manifest, or two sharing a name, is
401
+ * matched and left out, not unmatched (M-46).
402
+ */
403
+ export async function reachesManifest(workspace: Workspace): Promise<boolean> {
404
+ const { positive, negative } = splitPackageGlobs(workspace.packageGlobs)
405
+ const root = path.resolve(workspace.root)
406
+ for (const pattern of positive) {
407
+ for (const dir of await memberDirs(workspace.root, pattern)) {
408
+ if (path.resolve(dir) === root) continue
409
+ if (negative.length > 0 && excludedBy(relPosix(workspace.root, dir), negative)) continue
410
+ try {
411
+ if ((await stat(path.join(dir, 'package.json'))).isFile()) return true
412
+ } catch (err) {
413
+ absent(err, undefined)
414
+ }
415
+ }
416
+ }
417
+ return false
418
+ }
419
+
398
420
  /**
399
421
  * The directories a workspace glob names. For the `<dir>/*` shape this is
400
422
  * one readdir of `<dir>` — the same answer `Bun.Glob` gives, at a third of