@vzn/vx 0.0.609 → 0.0.611
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/cache/inputs.ts +2 -1
- package/src/cli/lock.ts +9 -3
- package/src/cli/show.ts +7 -3
- package/src/cli/task-verb.ts +4 -1
- package/src/util/index.ts +1 -1
- package/src/util/real-path.ts +49 -1
- package/src/workspace/workspace.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.611",
|
|
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.
|
|
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.611",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.611",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.611",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.611"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/inputs.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
isLiteralPattern,
|
|
29
29
|
normalizeGlob,
|
|
30
30
|
outputMatcher,
|
|
31
|
+
realpathOf,
|
|
31
32
|
relPosix,
|
|
32
33
|
shellArgv,
|
|
33
34
|
slashBraceExpansions,
|
|
@@ -1142,7 +1143,7 @@ export async function cleanWorkspaceOutputs(args: {
|
|
|
1142
1143
|
|
|
1143
1144
|
function realOrNull(p: string): string | null {
|
|
1144
1145
|
try {
|
|
1145
|
-
return
|
|
1146
|
+
return realpathOf(p)
|
|
1146
1147
|
} catch {
|
|
1147
1148
|
return null
|
|
1148
1149
|
}
|
package/src/cli/lock.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { relPosix, secretMask, xxh3hex } from '../util/index.js'
|
|
|
8
8
|
import {
|
|
9
9
|
findWorkspaceRoot,
|
|
10
10
|
type LoadReads,
|
|
11
|
-
listProjects,
|
|
12
11
|
loadProjectConfig,
|
|
13
12
|
loadWorkspace,
|
|
14
13
|
LOCKFILE_NAME,
|
|
@@ -19,6 +18,7 @@ import {
|
|
|
19
18
|
type LockfileEntry,
|
|
20
19
|
type ProjectMeta,
|
|
21
20
|
} from '../workspace/index.js'
|
|
21
|
+
import { discoverCliProjects } from './workspace-config.js'
|
|
22
22
|
|
|
23
23
|
interface LockArgs {
|
|
24
24
|
check: boolean
|
|
@@ -49,7 +49,13 @@ export async function lockCmd(args: readonly string[]): Promise<number> {
|
|
|
49
49
|
const reads: LoadReads = new Map()
|
|
50
50
|
const root = await findWorkspaceRoot(process.cwd(), reads)
|
|
51
51
|
const workspace = await loadWorkspace(root, reads)
|
|
52
|
-
|
|
52
|
+
// As a run discovers them: a project a plugin's `discover` stage adds
|
|
53
|
+
// was left out of the lock, and every `--frozen` run then refused it
|
|
54
|
+
// with "run vx lock", which changed nothing (X-143).
|
|
55
|
+
// Sorted by name, as core's discovery is, for stable lockfile diffs.
|
|
56
|
+
const all = (await discoverCliProjects(workspace)).sort((a, b) =>
|
|
57
|
+
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
|
|
58
|
+
)
|
|
53
59
|
const metas = all.filter((m): m is ConfiguredMeta => m.configPath !== null)
|
|
54
60
|
// A project with no vx.config has nothing to freeze: its tasks (a plugin's,
|
|
55
61
|
// or none) load live under --frozen too, so both verbs say how many the
|
|
@@ -83,7 +89,7 @@ function bareNote(bare: number): string {
|
|
|
83
89
|
|
|
84
90
|
async function writeLock(root: string, metas: ConfiguredMeta[], bare: number): Promise<number> {
|
|
85
91
|
const entries = await Promise.all(metas.map((m) => evaluateEntry(root, m)))
|
|
86
|
-
//
|
|
92
|
+
// Sorted by name above — stable lockfile diffs. Defined,
|
|
87
93
|
// not assigned: a project named `__proto__` set the map's prototype (D-152).
|
|
88
94
|
const projects: Record<string, LockfileEntry> = Object.fromEntries(
|
|
89
95
|
metas.map((m, i) => [m.name, entries[i]!]),
|
package/src/cli/show.ts
CHANGED
|
@@ -154,13 +154,17 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
if (bareTask) {
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
// As `vx run <task> --all` takes it: the default build is declared by no one (X-145).
|
|
158
|
+
const declares = (task: TaskConfig | undefined): boolean =>
|
|
159
|
+
task !== undefined && !isDefaultBuild(task)
|
|
160
|
+
const declaring = [...projects.values()].filter((p) =>
|
|
161
|
+
declares(declaredTask(p.config, projectName!)),
|
|
159
162
|
)
|
|
160
163
|
if (declaring.length === 0) {
|
|
161
164
|
const names = new Set<string>()
|
|
162
165
|
for (const p of projects.values())
|
|
163
|
-
for (const t of Object.
|
|
166
|
+
for (const [t, task] of Object.entries(p.config.tasks ?? {}))
|
|
167
|
+
if (declares(task)) names.add(t)
|
|
164
168
|
// `nx show projects` lists them; here a bare `vx show` does.
|
|
165
169
|
const nx = projectName === 'projects' ? ' (`nx show projects` is `vx show` here)' : ''
|
|
166
170
|
throw new UserError(
|
package/src/cli/task-verb.ts
CHANGED
|
@@ -25,7 +25,10 @@ export async function taskVerbHint(
|
|
|
25
25
|
const projects = await workspaceProjects(cwd)
|
|
26
26
|
if (projects === null) return null
|
|
27
27
|
const tasks = new Set<string>()
|
|
28
|
-
|
|
28
|
+
// The default build is no task `vx run` takes by name (X-145).
|
|
29
|
+
for (const p of projects.values())
|
|
30
|
+
for (const [t, task] of Object.entries(p.config.tasks ?? {}))
|
|
31
|
+
if (!isDefaultBuild(task)) tasks.add(t)
|
|
29
32
|
const task = tasks.has(command) ? command : typo ? nearest(command, tasks) : undefined
|
|
30
33
|
if (task === undefined) return null
|
|
31
34
|
// Nx's `nx build app`: the word after the target is its project.
|
package/src/util/index.ts
CHANGED
|
@@ -74,7 +74,7 @@ export { isUnsupportedBun, MIN_BUN, unsupportedBunMessage } from './bun-version.
|
|
|
74
74
|
export { executablePath, shellArgv, taskShell } from './which.js'
|
|
75
75
|
export { procfsIsOwn } from './procfs.js'
|
|
76
76
|
export { hangupIgnored } from './hangup.js'
|
|
77
|
-
export { realPath } from './real-path.js'
|
|
77
|
+
export { realPath, realpathOf } from './real-path.js'
|
|
78
78
|
export {
|
|
79
79
|
maskedCommand,
|
|
80
80
|
maskedEmitter,
|
package/src/util/real-path.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { realpathSync } from 'node:fs'
|
|
1
|
+
import { readlinkSync, realpathSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* A path's canonical spelling: the OS's own answer for the final path,
|
|
@@ -7,3 +8,50 @@ import { realpathSync } from 'node:fs'
|
|
|
7
8
|
export function realPath(p: string): string {
|
|
8
9
|
return realpathSync.native(p)
|
|
9
10
|
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `realpathSync`, for a path holding a backslash too: Bun's answers ENOENT
|
|
14
|
+
* for any such path (1.4.2), so an output under `dist/a\b/` was dropped
|
|
15
|
+
* from its artifact. Such a path's links are read one by one. Throws as
|
|
16
|
+
* `realpathSync` does when the path is not there.
|
|
17
|
+
*/
|
|
18
|
+
export function realpathOf(p: string): string {
|
|
19
|
+
try {
|
|
20
|
+
return realpathSync(p)
|
|
21
|
+
} catch (err) {
|
|
22
|
+
if (!p.includes('\\')) throw err
|
|
23
|
+
return resolveLinks(p)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveLinks(p: string): string {
|
|
28
|
+
// Not `path.resolve`: it folds `link/..` lexically, and the kernel
|
|
29
|
+
// takes `..` from where the link leads.
|
|
30
|
+
const abs = path.isAbsolute(p) ? p : `${process.cwd()}${path.sep}${p}`
|
|
31
|
+
let at = path.parse(abs).root
|
|
32
|
+
const pending = abs.slice(at.length).split(path.sep).filter(Boolean).reverse()
|
|
33
|
+
let hops = 0
|
|
34
|
+
while (pending.length > 0) {
|
|
35
|
+
const next = path.join(at, pending.pop()!)
|
|
36
|
+
let link: string
|
|
37
|
+
try {
|
|
38
|
+
link = readlinkSync(next)
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if ((err as NodeJS.ErrnoException).code !== 'EINVAL') throw err
|
|
41
|
+
at = next
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
// Linux's own bound on links followed in one lookup (MAXSYMLINKS).
|
|
45
|
+
if (++hops > 40) {
|
|
46
|
+
throw Object.assign(
|
|
47
|
+
new Error(`ELOOP: too many symbolic links encountered, realpath '${p}'`),
|
|
48
|
+
{
|
|
49
|
+
code: 'ELOOP',
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
if (path.isAbsolute(link)) at = path.parse(link).root
|
|
54
|
+
pending.push(...link.split('/').filter(Boolean).reverse())
|
|
55
|
+
}
|
|
56
|
+
return at
|
|
57
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
slashBraceExpansions,
|
|
13
13
|
UserError,
|
|
14
14
|
normalizeBunGlob,
|
|
15
|
+
realpathOf,
|
|
15
16
|
} from '../util/index.js'
|
|
16
17
|
import { type LoadReads, readOnce, unreadable } from './load-reads.js'
|
|
17
18
|
import { repoIdOf } from './repo-id.js'
|
|
@@ -897,7 +898,7 @@ async function oneEntryPerPackage<T extends { dir: string }>(
|
|
|
897
898
|
): Promise<T[]> {
|
|
898
899
|
const byReal = new Map<string, T[]>()
|
|
899
900
|
for (const entry of group) {
|
|
900
|
-
const real =
|
|
901
|
+
const real = realpathOf(entry.dir)
|
|
901
902
|
const same = byReal.get(real)
|
|
902
903
|
if (same === undefined) byReal.set(real, [entry])
|
|
903
904
|
else same.push(entry)
|